가로/세로 방향의 공간을 줄이는 연산
최대 풀링(Max Pooling) 처리 순서
최대 풀링은 최댓값을 구하는 연산으로 ' 2 X 2 '는 대상 영역의 크기를 뜻하고 위의 그림과 같이 2 X 2 크기의 영역에서 가장 큰 원소를 하나 꺼낸다.
스트라이드는 이 에에서 2로 설정했으므로 2 X 2 윈도우가 원소 2칸 간격으로 이동하는데 최대 풀링의 경우 대체로 윈도우 크기와 스트라이드 크기는 같은 값으로 설정한다.
만약 윈도우가 3 X 3의 경우는 스트라이드를 3으로 윈도우가 4 X 4인 경우에는 스트라이드를 4로 설정한다.
CNN에서 계층 사이를 흐르는 데이터는 4차원이다.
import numpy as np
x = np.random.rand(10, 1, 28, 28) #무작위로 데이터 생성 (Data, Channel, Height,Width)
x.shape
> (10, 1, 28, 28)
print(x[0].shape) # 첫번째 데이터
print(x[1].shape) # 두번째 데이터
>(1, 28, 28)
>(1, 28, 28)
합성곱 연산을 곧이곧대로 구현하려면 for 문을 겹겹이 써야하는데 이는 numpy에서 for 문을 사용하면 성능이 떨어진다는 단점도 존재하여 im2col이라는 편의 함수를 사용해 간단하게 구현한다.
위에서는 스트라이드를 크게 잡아 필터의 적용 영역이 겹치지 않도록 했지만 실제 상황에서는 영역이 겹치는 경우가 많다.
4-1) 필터를 세로로 1열로 전개
4-2) im2col이 전개한 데이터와 행렬 곱을 계산
4-3) 출력 데이터를 reshape 실행
위와 같이 im2col 방식으로 출력한 결과는 2차원 행렬이므로 CNN 데이터 배열을 맞춰주기 위해 reshape를 통해 4차원으로 변형 시켜준다.
------------------------------------------------------------------
im2col( input_data, filter_h, filter_w, stride = 1, pad = 0)
#input_data = (데이터의 수, 채널 수 높이, 너비)로 이루어진 4차원 데이터
#filter_h : 필터의 높이
#filter_w : 필터의 너비
#stride : 스트라이드
#pad : 패딩
------------------------------------------------------------------
import sys, os
sys.path.append(os.pardir)
from common.util import im2col
x1 = np.random.rand(1, 3, 7, 7) #(데이터 수 , 채널 수, 높이, 너비) 4차원 데이터 생성
col1 = im2col(x1, 5, 5, stride = 1, pad = 0)
print(col1. shape)
> (9, 75)
x2 = np.random.rand(10, 3, 7, 7) # x1과 같은 데이터 10개 생성
col2 = im2col(x2, 5, 5, stride = 1, pad = 0)
print(col2.shape)
> (90, 75)
x2의 90은 x1에서 9인 원소가 10개 있으므로 90이 되고 2번째 차원은 필터의 크기와 채널 수를 곱한 원소의 개수는 동일하므로 변하지 않는다.
class Convolution:
def __init__(self, W, b, stride = 1, pad = 0)
self.W = W
self.b = b
self.stride = stride
self.pad = pad
def forward(self, x):
FN, C, FH, FW = self.W.shape
N, C, H, W = x.shape
out_h = int(1 + (H + 2 * self.pad - FH) / self.stride)
out_w = int(1 + (W + 2 * self.pad - FW) / self.stride)
col = im2col(x, FH, FW, self.stride, self.pad)
col_W = self.W.rehape(FN, -1).T #필터 전개
#reshape의 두 번째 인수 -1은 reshape의 편의 기능으로 다차원 배열의 원소 수가 변환 후에도 똑같이 유지되도록 적절히 묶어주는 역할을 한다.
out = np.dot(col, col_W) + self.b
out = out.reshape(N, out_h, out_w, -4).transpose(0, 3, 1, 2)
#transpose 함수는 다차원 배열의 축 순서를 바꿔주는 함수이다.
return out
FN : 필터 개수
C : 채널
FH : 필터 높이
FW : 필터 너비
(10, 3, 5, 5)의 형상을 한 다차원 배열 W의 원소 수는 총 750개 (10 x 3 x 5 x 5 = 750)
이때 reshape(10, -1)을 호출하면 750개의 원소를 10묶음으로 만들어 (10, 75)인 배열로 만들어준다.
변경전 변경후 형상 (N, H, W, C) (N, C, H, W) 인덱스 0, 1, 2, 3 (0, 3, 1, 2)
Class Polling:
def __init__(self, pool_h, pool_w, stride = 1, pad = 0)
self.pool_h = pool_h
self.pool_w = pool_w
self.stride = stride
self.pad = pad
def forward(self, x):
N, C, H, W = x.shape
out_h = int(1+ (H - self.pool_h) / self.stride)
out_w = int(1+ (W - self.pool_w) / self.stride)
# 전개 (1)
col = im2col(x, self.pool_h, selfpool_w, self.stride, self.pad)
col = col.reshape(-1, self.pool_h * self.pool_w)
# 최댓값 (2)
out = np.max(col, axis = 1)
# 성형 (3)
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
return out