오늘은 Convolutional Layer에서 합성곱 연산을 효율적으로 하기 위한 방법을 다룰 것이다.
CNN에서 Layer 사이에 흐르는 데이터는 Fully connected layer의 데이터와 달리 4차원 데이터이다. 형상이 (10, 1, 28, 28)이라면 (데이터 수, 채널 수, 높이, 너비)순으로 높이 28, 너비 28, 채널 1개인 데이터가 10개라는 것이다. CNN에서 convolutional layer에서는 합성곱 연산을 하는데, 4차원 데이터를 각 window마다 합성곱하여 배열에 넣고 더하는 과정은 많은 시간을 요구하며 다중 for문을 사용해야 하므로 코드가 복잡해진다.
이러한 convolution연산의 문제를 해결하기 위해 im2col(image to column)이라는 트릭을 사용할 것이다. colvolution의 연산은 위 그림을 보면, 3x3필터는 입력 데이터의 특정 픽셀들과 곱해진다. 예를 들어 필터의 우측 상단 원소인 2는 입력 데이터의 좌측 하단 원소인 1과 연산하지 않는다. 필터와 연산하는 부분이 정해져있으니, 그 부분을 각각 빼내어 필터와 행렬연산을 하도록 데이터 형상을 조정하는 것이 im2col의 아이디어이다. 즉 im2col은 입력 데이터를 가중치 계산하기 편하도록 4차원 이미지인 입력 데이터와 filter를 모두 2차원 행렬로 전개하는 함수이다.
이처럼 im2col방법을 사용하면 convolution연산을 할 때, 필터와의 연산 부분을 펼치므로 픽셀이 중복되어 공간 복잡도는 커지지만, 약 200배 더 빠른 연산 속도를 가지므로 시간 복잡도에서 얻는 이점이 훨씬 크다.
CNN은 매 Layer에서 4차원으로 데이터를 처리하기 때문에 다음 Layer에 전달하기 위해서는 2차원 출력 데이터를 다시 N(데이터 개수) x OH(출력 데이터 높이) x OW(출력 데이터 폭) x FN(필터 개수) 4차원으로 변형해야 한다. 이를 reshape이라 한다.
<합성곱 연산 과정>그림에서는 위의 <im2col 적용>그림과 달리 입력 데이터가 row로 바뀌고 필터가 column으로 바뀌었다. 곱셈의 순서만 지켜주면 상관없는 부분이다.
<im2col 구현>
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
N, C, H, W = input_data.shape
out_h = (H + 2 * pad - filter_h) // stride + 1
out_w = (W + 2 * pad - filter_w) // stride + 1
img = np.pad(input_data, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant', constant_values=0)
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
for y in range(filter_h):
y_max = y + stride * out_h
for x in range(filter_w):
x_max = x + stride * out_w
col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N * out_h * out_w, -1)
return col
np.pad로 패딩하는 코드는 순서대로 [(데이터 개수 앞/뒤에 패딩), (필터 개수 앞/뒤에 패딩), (입력 데이터 위/아래에 패딩), (입력데이터 좌/우에 패딩)]를 의미한다. 데이터 개수와 필터 개수의 패딩은 (0, 0)으로 두어 제외시키고, 입력 데이터의 위/아래/좌/우에 (pad, pad)를 통해 pad를 적용하면 된다.
<im2col 적용 예시>
import sys, os
sys.path.append(os, pardir)
from common.util import im2col
x1 = np.random.rand(1, 3, 7, 7) #데이터 수, 채널 수, 높이, 너비
col1 = im2col(x1, 5, 5, stride = 1, pad = 0) #데이터 수, 필터 높이, 필터 너비, 스트라이드, 패딩)
print(col1.shape) #(9, 75)
x2 = np.random.rand(10, 3, 7, 7) #데이터 10개
col2 = im2col(x2, 5, 5, strid = 1, pad = 0)
print(col2.shape) #(90, 75)
출처: 사이토 고키, 『밑바닥부터 시작하는 딥러닝』, 한빛미디어(2017), p243-245.
'AI > CNN' 카테고리의 다른 글
딥러닝) CNN 풀링 계층 (0) | 2021.04.05 |
---|---|
딥러닝) CNN 구조 , 합성곱 계층 , padding , stride , 배치 처리 (0) | 2021.04.04 |
댓글