이전 포스트에서 합성곱 계층과 풀링 계층에 대해 이론적으로 살펴보았습니다.
2020/07/10 - [Computer Science/Deep Learning] - [비전공자용]합성곱 신경망 (CNN) - 합성곱 계층 & 풀링 계층
지금부터 두 계층을 직접 Python 코드로 구현하고자 합니다.
1. 합성곱 계층 구현
합성곱 계층에서는 image to column (통상적으로 im2col 함수로 표현)을 함수로 만들어서 합성곱 계층을 구현할 때 많이 이용합니다.
im2col로 입력 데이터를 전개하고 합성곱 계층의 필터를 1열로 전개해서
입력 데이터 X 필터 --> 두 행렬의 곱을 계산하면 됩니다. 완전연결 계층의 Affine 계층과 같은 방식으로 하면 됩니다.
im2col 함수를 먼저 Python 코드로 구현하면 아래와 같습니다.
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
"""다수의 이미지를 입력받아 2차원 배열로 변환한다.
Parameters
----------
input_data : 4차원 배열 형태의 입력 데이터(이미지 수, 채널 수, 높이, 너비)
filter_h : 필터의 높이
filter_w : 필터의 너비
stride : 스트라이드
pad : 패딩
Returns
-------
col : 2차원 배열
"""
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')
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
im2col로 입력 데이터를 2차원 배열로 전개하고 합성곱 계층을 Convolution이라는 클래스로 구현하면 아래와 같다.
class Convolution:
def __init__(self, W, b, stride=1, pad=0):
self.W = W # 필터(가중치)
self.b = b # 편향 bias
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.reshape(FN, -1).T # 2차원 배열로 필터 전개
out = np.dot(col, col_W) + self.b # 전개된 두 행렬의 곱
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
# (N, H, W, C) ---> (N, C, H, W)
# 0, 1, 2, 3 ---> 0, 3, 1, 2 로 transpose 이용해서 인덱스로 축 순서 변경해줌
return out
위 코드에서 reshape(FN, -1)에 대해 설명하고 지나가겠습니다.
원래 self.W인 필터는 (FN, C, FH, FW)의 4차원 형상입니다.
reshape 함수를 이용해서 2차원 배열인 (FN, -1)로 전개하게 한다는 것은
결국 (FN, FN*C*FH*FW/FN) 의 형상의 배열로 만들어준다는 얘기가 됩니다.
더 이해가 잘 가도록 예를 들어서, A = (10, 5, 4, 3)의 형상을 한 다차원 배열 A가 있다고 합시다.
배열 A의 총 원소의 수는 10*5*4*3=600개죠?
A.reshape(10, -1)는 (10, 60)의 형상을 가진 배열로 만들어주면서
형상이 변환된 후에도 원소 수가 똑같이 유지가 됩니다.
여기까지 reshape 함수에 대한 설명이었고요, 마저 Convolution 합성곱 계층을 구현해봅시다.
backward 역전파 구현도 Convolution 클래스 내에서 해야 하는데 이 부분은 Affine 계층과 동일하고 im2col이 아니라 col2im을 사용해야 한다는 점만 다릅니다. 아래 코드를 확인해 보세요.
class Convolution:
def __init__(self, W, b, stride=1, pad=0):
self.W = W
self.b = b
self.stride = stride
self.pad = pad
# 중간 데이터(backward 시 사용)
self.x = None
self.col = None
self.col_W = None
# 가중치와 편향 매개변수의 기울기
self.dW = None
self.db = None
def forward(self, x):
FN, C, FH, FW = self.W.shape
N, C, H, W = x.shape
out_h = 1 + int((H + 2*self.pad - FH) / self.stride)
out_w = 1 + int((W + 2*self.pad - FW) / self.stride)
col = im2col(x, FH, FW, self.stride, self.pad)
col_W = self.W.reshape(FN, -1).T
out = np.dot(col, col_W) + self.b
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
self.x = x
self.col = col
self.col_W = col_W
return out
def backward(self, dout):
FN, C, FH, FW = self.W.shape
dout = dout.transpose(0,2,3,1).reshape(-1, FN)
self.db = np.sum(dout, axis=0)
self.dW = np.dot(self.col.T, dout)
self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)
dcol = np.dot(dout, self.col_W.T)
dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)
return dx
참고로 col2im 함수를 파이썬으로 구현하면 아래와 같습니다.
def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
"""(im2col과 반대) 2차원 배열을 입력받아 다수의 이미지 묶음으로 변환한다.
Parameters
----------
col : 2차원 배열(입력 데이터)
input_shape : 원래 이미지 데이터의 형상(예:(10, 1, 28, 28))
filter_h : 필터의 높이
filter_w : 필터의 너비
stride : 스트라이드
pad : 패딩
Returns
-------
img : 변환된 이미지들
"""
N, C, H, W = input_shape
out_h = (H + 2*pad - filter_h)//stride + 1
out_w = (W + 2*pad - filter_w)//stride + 1
col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)
img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))
for y in range(filter_h):
y_max = y + stride*out_h
for x in range(filter_w):
x_max = x + stride*out_w
img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]
return img[:, :, pad:H + pad, pad:W + pad]
지금까지 Python을 이용해서 합성곱 계층을 구현하는 방법을 알아봤습니다.
다음 포스트에서는 Python을 이용해서 풀링 계층을 구현하는 방법을 알아보도록 하겠습니다~~
2020/07/28 - [Computer Science/Deep Learning] - [비전공자용] [Python] CNN(합성곱 신경망) - 풀링 계층 구현
[출처] Deep Learning from Scratch, ゼロ から作る
'Computer Science > Deep Learning' 카테고리의 다른 글
[비전공자용] [Python] CNN 합성곱 신경망 구현하기 (4) | 2020.07.28 |
---|---|
[비전공자용] [Python] CNN(합성곱 신경망) - 풀링 계층 구현 (0) | 2020.07.28 |
[비전공자용]합성곱 신경망 (CNN) - 합성곱 계층 & 풀링 계층 (2) | 2020.07.10 |
[비전공자용] [Python] 하이퍼파라미터 최적화 Hyperparameter Optimization (0) | 2020.07.10 |
[비전공자용] 오버피팅 Overfitting 억제법 - 1.가중치 감소 2.드롭아웃 Dropout (0) | 2020.07.10 |