Machine Learning/[Kaggle Course] ML (+ 딥러닝, 컴퓨터비전)

Maximum Pooling - feature extraction

WakaraNai 2021. 4. 9. 02:28
728x90
반응형

!!여기서 활성화 activation의 의미란, 

이미지 행렬 안의 숫자값이 0이 아니고 색깔이나 의미를 가진 0 외의 숫자로 되어있다는 의미.

0이 있는 곳은 검은색으로 표시됨

 

 

이번 시간에는 convnet 속 base이 feature extraction을 수행하는 방법에 대해 보려 합니다.

이전까지 feature extraction에는 conv2d layer와 relu activation으로 2가지 과정이 일어난다고 했습니다.

 

이번에는 세번째 연산 과정을 보려합니다.

maximum pooling을 이용한 압축(condense)입니다.

이는 케라스의 MaxPool2D layer에서 일어납니다.

 

 

Condense with Maximum Pooling

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Conv2D(filters=64, kernel_size=3), # activation is None
    layers.MaxPool2D(pool_size=2),
    # More layers follow
])

 

MaxPool2D layer는 Conv2D layer와 매우 유사하지만, 커널 대신 간단한 최대화 함수를 사용한다는 점을 다릅니다.

최대화 함수는 kernel_size와 유사한 pool_size라는 매개변수를 가집니다.

그러나 convolutional layer에서 커널이 가중치를 훈련하는 것을 MaxPool2D layer는 하진 못합니다.

 

 

 저번 시간에 사용한 예제에 적용해보겠습니다.

MaxPool2D는 Condense 단계라는 것을 명심하세요.

 

 

ReLU 함수 (Detect) 적용 후에 feature map에 검은색 공간이 많이 생긴 점, 즉 많은 영역이 0으로 표기된 점이 눈에 띄입니다. 

이 숫자 0들을 네트워크에 통과하면 더이상은 정보를 추가하지 않고 모델의 크기를 늘릴 수 있습니다.

대신, feature map이 그 feature의 세세한 정보를 잘 살릴 수 있는 유용한 부분만 가져올 수 있도록 압축해야 합니다.

 

이 과정을 Maximum Pooling이 해줍니다.

초기의 feature map 속 활성화된 부분을 가져와서 활성화값의 최대치로 그 부분의 값을 바꾸어주는 역할을 합니다.

 

특히 ReLU 활성화 함수 이후에 적용하면 특징이 더욱 강조됩니다. pooling 단계는 활성화된 활성화된 픽셀의 비율을 0픽셀만큼 되도록 증가시킵니다.

 

 

Example

import matplotlib.pyplot as plt
import warnings

plt.rc('figure', autolayout=True)
plt.rc('axes', labelweight='bold', labelsize='large',
       titleweight='bold', titlesize=18, titlepad=10)
plt.rc('image', cmap='magma')
warnings.filterwarnings("ignore") # to clean up output cells

# Read image
image_path = '../input/computer-vision-resources/car_feature.jpg'
image = tf.io.read_file(image_path)
image = tf.io.decode_jpeg(image)

# Define kernel
kernel = tf.constant([
    [-1, -1, -1],
    [-1,  8, -1],
    [-1, -1, -1],
], dtype=tf.float32)

# Reformat for batch compatibility.
image = tf.image.convert_image_dtype(image, dtype=tf.float32)
image = tf.expand_dims(image, axis=0)
kernel = tf.reshape(kernel, [*kernel.shape, 1, 1])

# Filter step
image_filter = tf.nn.conv2d(
    input=image,
    filters=kernel,
    # we'll talk about these two in the next lesson!
    strides=1,
    padding='SAME'
)

# Detect step
image_detect = tf.nn.relu(image_filter)

# Show what we have so far
plt.figure(figsize=(12, 6))
plt.subplot(131)
plt.imshow(tf.squeeze(image), cmap='gray')
plt.axis('off')
plt.title('Input')
plt.subplot(132)
plt.imshow(tf.squeeze(image_filter))
plt.axis('off')
plt.title('Filter')
plt.subplot(133)
plt.imshow(tf.squeeze(image_detect))
plt.axis('off')
plt.title('Detect')
plt.show();

 

 

pooling 단계를 적용하기 위해 tf.nn 속에 있는 tf.nn.pool 함수를 사용하려 합니다.

이는 파이썬 함수로 모델을 만들 때 사용하는 MaxPool2D layer와 같은 일을 하지만 그보다 간단하고 쉬운 함수입니다.

출력된 결과를 보면 pooling 단계가 특징을 강조한 것을 확인할 수 있습니다.

가장 많이 활성화된, 큰 값을 가진 픽셀 주위로 이미지가 압축, 응축 되었습니다.

import tensorflow as tf

image_condense = tf.nn.pool(
    input=image_detect, # image in the Detect step above
    window_shape=(2, 2),
    pooling_type='MAX',
    # we'll see what these do in the next lesson!
    strides=(2, 2),
    padding='SAME',
)

plt.figure(figsize=(6, 6))
plt.imshow(tf.squeeze(image_condense))
plt.axis('off')
plt.show();

 

 

Translation Invariance

0 pixel은 "중요하지 않음"이라고도 부릅니다.

그렇다고 아무 정보도 주지 않는 것은 아닙니다. 위치에 대한 정보는 중요하죠.

빈 공간은 이미지 속에서 특징으로 간주됩니다. 

MaxPool2D가 이러한 픽셀들을 삭제할 때 feature map 속 위치 정보도 삭제됩니다

이로 인해 'translation invariance'라고 불리는 속성이 convnet에 추가됩니다.

그 의미는 maximum pooling을 가진 convnet은 이미지 속 위치에 따라 특징을 구별하지 않음을 의미합니다.

('Translation'은 수학적 용어로, 이미지의 모양이나 크기의 변화 또는 회전 없이 위치를 바꿔 줍니다.)

 

maximum pooling을 적용한 각 feature map의 결과

원본 이미지에서 두 점은 pooling을 여러 번 반복하고 나니 구별하기 어려워졌습니다.

즉, pooling은 위치 정보를 파괴합니다.

네트워크가 더이상 feature maps 속 두 점을 구별할 수 없기 때문에, 원본 이미지를 주어도 구별할 수 없습니다.

 

사실은, pooling은 위의 두 점처럼 오직 작은 거리 차이 정도만 network 속에 translation invariance, 즉 이미지 속 세세한 차이, 위치 정보를 무시할 수 있습니다. 

멀리 떨어진 특징은 pooling 이후에도 구별 가능합니다.

약간의 위치 정보는 잃어버리겠지만 전부는 아닙니다.

 

 

특징들의 위치 속 작은 거리 차이는 불변합니다. 이는 좋은 이미지 분류기를 가지기에 멋진 속성입니다.

사람의 관점이나 프레임 속 차이 때문에 같은 종류의 특징들일 지라도 원본 이미지 속에서 다양한 곳에 위치할 수 있습니다. 위의 사진처럼요,

하지만 분류기는 이 차이에도 불구하고 그들이 같다고 인식하게 됩니다.

 

왜냐하면 차이를 무시하는 불변성을 네트워크에 만들었지만, 훈련에 더 적은 양의 데이터를 사용하기 때문입니다.

더 이상 차이를 무시하는 훈련을 할 필요가 없어지니깐요,

이는 convolutional 네트워크에게 커다란 장점을 줍니다. 특히 오직 dense layer를 가진 하나의 네트워크에서요.

(무료로 불변성, 차이 무시를 습득하는 방법은 Data Augmentation을 참고하세요)

 

 


 

 

Exercise

invariance(차이 무시하는 불변성) 기능을 적용하기 위해

maximum pooling을 본 뒤, average pooling도 한 번 보겠습니다.

 

# Setup feedback system
from learntools.core import binder
binder.bind(globals())
from learntools.computer_vision.ex3 import *

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from matplotlib import gridspec
import learntools.computer_vision.visiontools as visiontools

plt.rc('figure', autolayout=True)
plt.rc('axes', labelweight='bold', labelsize='large',
       titleweight='bold', titlesize=18, titlepad=10)
plt.rc('image', cmap='magma')

 

저번 시간에 사용해본 미리 정의한 커널을 가져오기 위한 코드입니다.

# Read image
image_path = '../input/computer-vision-resources/car_illus.jpg'
image = tf.io.read_file(image_path)
image = tf.io.decode_jpeg(image, channels=1)
image = tf.image.resize(image, size=[400, 400])

# Embossing kernel
kernel = tf.constant([
    [-2, -1, 0],
    [-1, 1, 1],
    [0, 1, 2],
])

# Reformat for batch compatibility.
image = tf.image.convert_image_dtype(image, dtype=tf.float32)
image = tf.expand_dims(image, axis=0)
kernel = tf.reshape(kernel, [*kernel.shape, 1, 1])
kernel = tf.cast(kernel, dtype=tf.float32)

image_filter = tf.nn.conv2d(
    input=image,
    filters=kernel,
    strides=1,
    padding='VALID',
)

image_detect = tf.nn.relu(image_filter)

# Show what we have so far
plt.figure(figsize=(12, 6))
plt.subplot(131)
plt.imshow(tf.squeeze(image), cmap='gray')
plt.axis('off')
plt.title('Input')
plt.subplot(132)
plt.imshow(tf.squeeze(image_filter))
plt.axis('off')
plt.title('Filter')
plt.subplot(133)
plt.imshow(tf.squeeze(image_detect))
plt.axis('off')
plt.title('Detect')
plt.show();

 

1. 압축(Condense)를 위해 pooling 적용하기

2x2 사이즈의 pooling window를 이용하여 maximum pooling을 적용하려 합니다.

# YOUR CODE HERE
image_condense = tf.nn.pool(
    input=image_detect,
    window_shape=(2,2),
    pooling_type='MAX',
    strides=(2, 2),
    padding='SAME',
)

# Check your answer
q_1.check()

 

feature에 maximum pooling을 적용한 결과를 보여주는 코드입니다.

plt.figure(figsize=(8, 6))
plt.subplot(121)
plt.imshow(tf.squeeze(image_detect))
plt.axis('off')
plt.title("Detect (ReLU)")
plt.subplot(122)
plt.imshow(tf.squeeze(image_condense))
plt.axis('off')
plt.title("Condense (MaxPool)")
plt.show();

 

 

이번에는 MaxPool2D 레이어가 convolutional 네트워크에 작은 거리에 대한 translation invariance 속성을 주는 방식에 배워보려합니다. 아래 코드는 이를 관찰할 수 있는 코드입니다.

 

원을 조금씩 움직이며 무작위로 적용하고 maximum pooling을 이용하여 여러 번 이미지를 응축/압축합니다.

REPEATS = 4
SIZE = [64, 64]

# Create a randomly shifted circle
image = visiontools.circle(SIZE, r_shrink=4, val=1)
image = tf.expand_dims(image, axis=-1)
image = visiontools.random_transform(image, jitter=3, fill_method='replicate')
image = tf.squeeze(image)

plt.figure(figsize=(16, 4))
plt.subplot(1, REPEATS+1, 1)
plt.imshow(image, vmin=0, vmax=1)
plt.title("Original\nShape: {}x{}".format(image.shape[0], image.shape[1]))
plt.axis('off')

# Now condense with maximum pooling several times
for i in range(REPEATS):
    ax = plt.subplot(1, REPEATS+1, i+2)
    image = tf.reshape(image, [1, *image.shape, 1])
    image = tf.nn.pool(image, window_shape=(2,2), strides=(2, 2), padding='SAME', pooling_type='MAX')
    image = tf.squeeze(image)
    plt.imshow(image, vmin=0, vmax=1)
    plt.title("MaxPool {}\nShape: {}x{}".format(i+1, image.shape[0], image.shape[1]))
    plt.axis('off')

 

무작위로 이동하기에 조금씩 결과가 달라지는 게 보이시나요? (maxpool 1,2,3 사진에 집중해보세요)

작은 거리에 대해 maximum pooling이 어떻게 translation invariance를 해내는지 배웠습니다.

이는 반복된 maximum pooling 후에 작은 이동(shift)은 사라짐을 의미합니다.

여러 번 실행할 수로 점점 결과는 같은 결과를 내놓게 됩니다. 

pooling 작업은 작은 translation을 파괴하기 때문입니다.

 

 

 

Global Average Pooling

이전 Exercise에서 average pooling은 convolutional base 내에서 maximum pooling으로 대체된다고 말했습니다.

그러나 average pooling 중에서도, convnet의 head에 사용되는 것도 있습니다.

이것이 바로 global average pooling입니다.

GlobalAvgPool2D layer는 네트워크의 head 속 종종 몇 개의 또는 모든 hidden Dense layer의 대안책으로 사용됩니다.

 

2차원 특징 데이터를 분류기가 필요해하는 1차원 데이터로 변환하기 위해 보통 base 뒤에 썼던  Flatten 레이어는 더이상 필요 없습니다.

이제 GlobalAvgPool2D 레이어가 이러한 기능을 합니다.

그러나 Flatten처럼 특징을 쌓아 올려두지 않는 대신에, 평균값을 이용해 전체 feature map을 간단히 대체합니다.

매우 파괴적이긴 하지만, 종종 잘 작동하며 모델에 들어가는 매개변수의 수를 줄일 수 있는 이점이 있습니다.

 

 

GlobalAvgPool2D가 무작위로 feature maps를 생성하는 과정을 봅시다.

이는 base에 의해 생성된 feature maps를 쌓아올린 stack을 평평하게(flatten) 만드는 과정을 이해해야 알 수 있습니다.

이 레이어가 어떻게 작동하는 감이 올 때까지 여러 번 코드를 실행해보세요.

feature_maps = [visiontools.random_map([5, 5], scale=0.1, decay_power=4) for _ in range(8)]

gs = gridspec.GridSpec(1, 8, wspace=0.01, hspace=0.01)
plt.figure(figsize=(18, 2))
for i, feature_map in enumerate(feature_maps):
    plt.subplot(gs[i])
    plt.imshow(feature_map, vmin=0, vmax=1)
    plt.axis('off')
plt.suptitle('Feature Maps', size=18, weight='bold', y=1.1)
plt.show()

# reformat for TensorFlow
feature_maps_tf = [tf.reshape(feature_map, [1, *feature_map.shape, 1])
                   for feature_map in feature_maps]

global_avg_pool = tf.keras.layers.GlobalAvgPool2D()
pooled_maps = [global_avg_pool(feature_map) for feature_map in feature_maps_tf]
img = np.array(pooled_maps)[:,:,0].T

plt.imshow(img, vmin=0, vmax=1)
plt.axis('off')
plt.title('Pooled Feature Maps')
plt.show();

 

1
2
3
4
5
6
7
8
9
10

 

 

각각의 5x5 feature maps는 하나의 값을 줄이기 때문에 global pooling은 이 특징들을 표현하는데 필요한 매개변수의 수를 줄입니다. 25배에 정도 달하는 상당한 절감!

 

 

 

 

Understand Pooled Features

이번에는 pooled features를 이해해보려 합니다

features를 하나의 값으로 pool한 이후에 말입니다, 여전히 head는 집단을 결정하기에 충분한 정보를 가지고 있을까요?

 

VGG16에 Car or Truck 데이터 속 이미지를 넘겨서 pooling 이후의 features를 조사해봅시다.

아래 코드는 모델을 정의하고 모델에 데이터를 올리는 작업을 합니다.

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.preprocessing import image_dataset_from_directory

# Load VGG16
pretrained_base = tf.keras.models.load_model(
    '../input/cv-course-models/cv-course-models/vgg16-pretrained-base',
)

model = keras.Sequential([
    pretrained_base,
    # Attach a global average pooling layer after the base
    layers.GlobalAvgPool2D(),
])

# Load dataset
ds = image_dataset_from_directory(
    '../input/car-or-truck/train',
    labels='inferred',
    label_mode='binary',
    image_size=[128, 128],
    interpolation='nearest',
    batch_size=1,
    shuffle=True,
)

ds_iter = iter(ds)

Found 5117 files belonging to 2 classes.

(두 집단으로 나뉜 걸 보아 자동차와 트럭을 제대로 분류하네요)

 

이미 GlobalAvgPool2D 레이어를 이미 훈련된 VGG16 base 이후에 부착했음을 명심하세요.

일반적으로 VGG16은 각 이미지에 대해 512개의 feature를 생성합니다.

GlobalAvgPool2D 레이어는 이러한 여러 개의 값들을 하나의 값으로 줄여줍니다.

줄여진 하나의 값은 "average pixel"이라고 부릅니다.

 

 

아래의 코드는 VGG16에 car or truck 데이터셋 속 이미지를 넣은 결과와

GlobalAvgPool2D가 생성한 512개의 average pixels를 보여줍니다.

여러 번 실행하여 car가 생성한 pixels와 truck이 생성한 pixels를 비교해가며 관찰해보세요

car = next(ds_iter)

car_tf = tf.image.resize(car[0], size=[128, 128])
car_features = model(car_tf)
car_features = tf.reshape(car_features, shape=(16, 32))
label = int(tf.squeeze(car[1]).numpy())

plt.figure(figsize=(8, 4))
plt.subplot(121)
plt.imshow(tf.squeeze(car[0]))
plt.axis('off')
plt.title(["Car", "Truck"][label])
plt.subplot(122)
plt.imshow(car_features)
plt.title('Pooled Feature Maps')
plt.axis('off')
plt.show();

 

1
2
3

 

 

결과로 나온 pooled features는 자동차와 트럭을 구분할 수 있나요?

pooled values를 어떻게 해석하셨나요?

이것이 어떻게 분류에 도움이 될까요?

 

힌트를 드리자면, VGG16 base은 512개의 feature maps를 하나의 이미지로부터 생성합니다. 이는 한 개의 바퀴나 창문으로 해석됩니다. Pooled feature maps 속 각각의 사각형은 하나의 특징을 표현합니다.

feature 속 큰 값은 무엇을 의미할까요?

 

 

 

각각의 feature map이 원본 이미지 속 뚜렷한 시각적 특징을 표현한다고 생각해봅시다. 아마 창문이나 바퀴가 됩니다.

feature map을 pooling하는 과정을 거치면 하나의 숫자가 나오는데 이는 그 특징에 대한 점수로 생각할 수 있습니다.

숫자가 크면 그 특징이 존재하고 작으면 없다면 의미죠.

자동차는 어떤 features의 집합에서 고득점을 했고 트럭은 다른 집합에서 고득점을 했씁니다.

 

이제 가공하지 않은 features를 이용하여 집단을 구성하는 대신, head가 GlobalAvgPool2D이 생성한 숫자를  이용하여 문제를 더 간단히 풀 수 있습니다.

 

Global average pooling은 현대 convnet에 사용됩니다.

장점은 모델 속 매개변수의 수를 굉장히 많이 줄여줌에도 불구하고 이미지에 그 특징의 존재 여부를 잘 구분해냅니다.

 

그러니 convolutional classifier를 생성할 때 써보세요!

 

 

 

정리

Condensing with maximum pooling의 장점

  1. 시각적 데이터에 다루면 효과적임
  2. dense networks와 비교했을 때 매개변수의 수를 줄임
  3. translation invariance
  4. 특징을 추출하는 동안 base뿐만 아니라 분류하고 있는 동안의 head에서도 사용 가능

 

 

728x90
반응형