๋ฐ์ํ
์ด๋ฒ์๋ Convolution Layer, Pooling Layer๋ฅผ ํ๋ฒ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
Convolution & Pooling Layer ๊ตฌํํด๋ณด๊ธฐ
4-Dimension Array (4์ฐจ์ ๋ฐฐ์ด)
Convolution Neural Network(CNN)์์ Layer ์ฌ์ด๋ฅผ ํ๋ฅด๋ ๋ฐ์ดํฐ๋ 4์ฐจ์์ ๋๋ค.
- ์๋ฅผ ๋ค์ด์ ๋ฐ์ดํฐ์ ํ์์ด (10, 1, 28, 28)์ด๋ฉด?
- Height(๋์ด): 28, Width(๋๋น): 28, Channel(์ฑ๋): 1๊ฐ์ธ ๋ฐ์ดํฐ๊ฐ 10๊ฐ๋ผ๋ ์ด์ผ๊ธฐ ์ ๋๋ค.
- ์ด๋ฅผ Python์ผ๋ก ๊ตฌํํ๋ฉด ์๋์ ์ฝ๋์ ๊ฐ์ต๋๋ค.
x = np.random.rand(10, 1, 28, 28) # ๋ฌด์์๋ก ๋ฐ์ดํฐ ์์ฑ
x[0, 0] # ๋๋ x[0][0] ์ฒซ๋ฒ์งธ ๋ฐ์ดํฐ์ ์ฒซ ์ฑ๋ ๊ณต๊ฐ ๋ฐ์ดํฐ์ ์ ๊ทผ
- ์ฌ๊ธฐ์์ 10๊ฐ์ ๋ฐ์ดํฐ์ค ์ฒซ ๋ฒ์งธ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ค๋ฉด? ๋จ์ํ x[0]์ด๋ผ๊ณ ์๋๋ค.
- Python์ index๋ 0๋ถํฐ ์์ํฉ๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก ๋ ๋ฒ์งธ ๋ฐ์ดํฐ๋ x[1] ์์น์ ์์ต๋๋ค.
x[0].shape # (1, 28, 28)
x[1].shape # (1, 28, 28)
- ๋ํ ์ฒซ๋ฒ์งธ ๋ฐ์ดํฐ์ ์ฒซ ์ฑ๋์ ๊ณต๊ฐ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ์ต๋๋ค.
x[0, 0] # or x[0][0]
im2col๋ก ๋ฐ์ดํฐ ์ ๊ฐํ๊ธฐ
im2col์ Input Data(์ ๋ ฅ ๋ฐ์ดํฐ)๋ฅผ filtering, ์ฆ Weight(๊ฐ์ค์น)๊ณ์ฐ์ ํ๊ธฐ ์ข๊ฒ ์ ๊ฐํ๋(ํผ์น๋) ํจ์์ ๋๋ค.
- ์๋์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด 3-Dimension Input Data์ im2col์ ์ ์ฉํ๋ฉด 2-Dimenion Array๋ก ๋ด๋๋๋ค.
- ์์ธํ๋ Batch ์์ Data ์๊น์ง ํฌํจํ 4-Dimension Data๋ฅผ 2-Dimension์ผ๋ก ๋ณํํฉ๋๋ค.
- im2col์ filtering ํ๊ธฐ ์ข๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฐํฉ๋๋ค.
- ๊ตฌ์ฒด์ ์ผ๋ก๋ ์๋์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ ๋ ฅ ๋ฐ์ดํฐ์์ filter๋ฅผ ์ ์ฉํ๋ ์์ญ(3-Dimension Block)์ ํ ์ค๋ก ๋์ฌ๋์ต๋๋ค.
- ์ด ์ ๊ฐ์์ filter๋ฅผ ์ ์ฉํ๋ ๋ชจ๋ ์์ญ์์ ์ํํ๋๊ฒ im2col ์ ๋๋ค.
- im2col๋ก Input Data๋ฅผ ์ ๊ฐํ ๋ค์์๋ Convolution Layer์ filter(weight)๋ฅผ 1์ด๋ก ์ ๊ฐํ๊ณ , ๋ Array์ ๊ณฑ์ ๊ณ์ฐํ๋ฉด ๋ฉ๋๋ค.
- ์ด๋ Fully-Connected Layer (FC)์ Affine ๊ณ์ธต์์ ํ ๊ฒ๊ณผ ๊ฑฐ์ด ๊ฐ์ต๋๋ค.
- ์์ ๊ทธ๋ฆผ์ filter๋ฅผ ์ธ๋ก๋ก 1์ด๋ก ์ ๊ฐํ๊ณ , im2col์ด ์ ๊ฐํ ๋ฐ์ดํฐ์ ํ๋ ฌ๊ณฑ์ ๊ณ์ฐํ, ์ถ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ๋ณํ(Reshape) ํฉ๋๋ค.
Convoultional Layer (ํฉ์ฑ๊ณฑ ๊ณ์ธต) ๊ตฌํํ๊ธฐ
ํ๋ฒ Convoultional Layer (ํฉ์ฑ๊ณฑ ๊ณ์ธต)์ ํ๋ฒ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
- im2col ํจ์์ Interface๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
im2col(input_data, filter_h, filter_w, stride=1, pad=0)
- input_data: (๋ฐ์ดํฐ ์, ์ฑ๋ ์, ๋์ด, ๋๋น)์ 4์ฐจ์ ๋ฐฐ์ด๋ก ์ด๋ค์ง ์ ๋ ฅ ๋ฐ์ดํฐ
- filter_h: ํํฐ์ ๋์ด
- filter_w: ํํฐ์ ๋๋น
- stride: ์คํธ๋ผ์ด๋
- pad: ํจ๋ฉ
- im2col์ 'ํํฐ ํฌ๊ธฐ, 'Stride', 'padding'์ ๊ณ ๋ คํ์ฌ ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ 2-Dimension์ผ๋ก ์ ๊ฐํฉ๋๋ค.
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, stride=1, pad=0)
print(col2.shape) # (90, 75)
- ์ฌ๊ธฐ์๋ 2๊ฐ์ง์ ์์๋ฅผ ๋ณด์ฌ์ค๋๋ค. ํ๋(x1)๋ Batch_size๊ฐ 1(๋ฐ์ดํฐ 1๊ฐ), Channel 3๊ฐ, ๋์ด * ๋๋น๊ฐ 7 x 7์ ๋ฐ์ดํฐ์ ๋๋ค.
- ๋ค๋ฅธ ํ๋(x2)๋ Batch_size๊ฐ 10(๋ฐ์ดํฐ 10๊ฐ)์ด๊ณ , ๋๋จธ์ง๋ ์ฒซ๋ฒ์งธ(x1)๊ณผ ๊ฐ์ต๋๋ค.
- im2col ํจ์๋ฅผ ์ ์ฉํ ๋ ๊ฒฝ์ฐ ๋ชจ๋ 2-Dimension์ ์์๋ 75์ ๋๋ค. ์ด ๊ฐ์ filter์ ์์ ์์ ๊ฐ์ต๋๋ค. (Channel 3๊ฐ, 5x5 ๋ฐ์ดํฐ)
- ๋ํ Batch_size๊ฐ 1์ผ๋ im2col์ ๊ฒฐ๊ณผ์ ํฌ๊ธฐ๊ฐ (9, 75)์ด๊ณ , 10์ผ ๋์๋ (90, 75) ํฌ๊ธฐ์ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋ฉ๋๋ค.
- ๊ทธ๋ฌ๋ฉด ํ๋ฒ im2col์ ์ฌ์ฉํ์ฌ Convolutional Layer๋ฅผ ํ๋ฒ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
class Convolution:
def __init__(self, W, b, stride=1, pad=0):
# ์ด๊ธฐํ ๋ฉ์๋
self.W = W # W๋ ํํฐ์ ๊ฐ์ค์น, 4์ฐจ์ ๋ฐฐ์ด: (ํํฐ ๊ฐ์, ์ฑ๋ ์, ํํฐ ๋์ด, ํํฐ ๋๋น)
self.b = b # b๋ ํํฐ์ ํธํฅ, 1์ฐจ์ ๋ฐฐ์ด: (ํํฐ ๊ฐ์,)
self.stride = stride # stride๋ ํํฐ๋ฅผ ์ ์ฉํ๋ ๊ฐ๊ฒฉ
self.pad = pad # pad๋ ์
๋ ฅ ๋ฐ์ดํฐ ์ฃผ๋ณ์ ๋ง๋ 0์ ๊ฐ์
def forward(self, x):
# ์์ ํ ๋ฉ์๋
FN, C, FH, FW = self.W.shape # FN: ํํฐ ๊ฐ์, C: ์ฑ๋ ์, FH: ํํฐ ๋์ด, FW: ํํฐ ๋๋น
N, C, H, W = x.shape # N: ๋ฐ์ดํฐ ๊ฐ์, C: ์ฑ๋ ์, H: ๋์ด, W: ๋๋น
out_h = int(1 + (H + 2*self.pad - FH) / self.stride) # ์ถ๋ ฅ ๋ฐ์ดํฐ์ ๋์ด ๊ณ์ฐ
out_w = int(1 + (W + 2*self.pad - FW) / self.stride) # ์ถ๋ ฅ ๋ฐ์ดํฐ์ ๋๋น ๊ณ์ฐ
# ์
๋ ฅ ๋ฐ์ดํฐ์ ํํฐ๋ฅผ 2์ฐจ์ ๋ฐฐ์ด๋ก ์ ๊ฐํ๊ณ ๋ด์ ํจ
col = im2col(x, FH, FW, self.stride, self.pad) # im2col ํจ์๋ ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํ๊ธฐ ์ข์ ํํ๋ก ๋ณํ
col_W = self.W.reshape(FN, -1).T # ํํฐ์ ๊ฐ์ค์น๋ฅผ 2์ฐจ์ ๋ฐฐ์ด๋ก ๋ณํ ํ ์ ์น
out = np.dot(col, col_W) + self.b # ๋ณํ๋ ์
๋ ฅ ๋ฐ์ดํฐ์ ํํฐ ๊ฐ์ค์น์ ๋ด์ ์ ๊ณ์ฐํ๊ณ ํธํฅ์ ๋ํจ
# ๊ฒฐ๊ณผ๋ฅผ ์ ์ ํ ๋ณํํ์ฌ ์ถ๋ ฅ ํํ๋ก ์กฐ์
# reshape์์ -1์ ์์์ ๊ฐ์์ ๋ง์ถฐ ์๋์ผ๋ก ํฌ๊ธฐ๋ฅผ ์ค์
out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
# transpose๋ฅผ ์ฌ์ฉํ์ฌ ์ถ์ ์์๋ฅผ ๋ณ๊ฒฝ (๋ฐฐ์น ํฌ๊ธฐ, ํํฐ ๊ฐ์, ๋์ด, ๋๋น ์์ผ๋ก ์กฐ์ )
return out
- Convolutional Layer๋ filter(Weight), Bias(ํธํฅ), Stride, Padding์ ์ธ์๋ก ๋ฐ์์ ์ด๊ธฐํํฉ๋๋ค.
- filter๋ (FN, C, FH, FW)์ 4-Dimension์ ํ์์ ๋๋ค. FN์ filter ๊ฐ์, C๋ Channel, FH๋ ํํฐ ๋์ด, FW๋ ํํฐ ๋๋น ์ ๋๋ค.
Pooling Layer ๊ตฌํํ๊ธฐ
Pooling Layer ๊ตฌํ๋ Convolutional Layer์ ๋ง์ฐฌ๊ฐ์ง๋ก im2col์ ์ฌ์ฉํด ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฐํฉ๋๋ค.
- ๋จ, Pooling์ ๊ฒฝ์ฐ์ Channel ์ชฝ์ด ๋ ๋ฆฝ์ ์ด๋ผ๋ ์ ์ด Convolutional Layer๋์๋ ๋ค๋ฆ ๋๋ค.
- ๊ตฌ์ฒด์ ์ผ๋ก๋ ์๋์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด Pooling ์ ์ฉ ์์ญ์ ์ฑ๋๋ง๋ค ๋ ๋ฆฝ์ ์ผ๋ก ์ ๊ฐํฉ๋๋ค.
- ์ด๋ ๊ฒ ์ ๊ฐ๋ฅผ ํ๊ณ , ์ ๊ฐํ ํ๋ ฌ์์ ํ ๋ณ ์ต๋๊ฐ์ ๊ตฌํ๊ณ ์ ์ ํ ํ์์ผ๋ก ์ฑํํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
- ์ด๊ฒ์ด Pooling Layer์ forward ์ฒ๋ฆฌ ํ๋ฆ์ ๋๋ค. ์ด๊ฑธ Python Code๋ก ํ๋ฒ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
class Pooling:
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 # ์
๋ ฅ ๋ฐ์ดํฐ ์ฃผ๋ณ์ ๋ง๋ 0์ ๊ฐ์
def forward(self, x):
# ์์ ํ ๋ฉ์๋
N, C, H, W = x.shape # N: ๋ฐ์ดํฐ ๊ฐ์, C: ์ฑ๋ ์, H: ๋์ด, W: ๋๋น
out_h = int(1 + (H - self.pool_h) / self.stride) # ์ถ๋ ฅ ๋ฐ์ดํฐ์ ๋์ด ๊ณ์ฐ
out_w = int(1 + (W - self.pool_w) / self.stride) # ์ถ๋ ฅ ๋ฐ์ดํฐ์ ๋๋น ๊ณ์ฐ
# ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ํ๋ง์ ์ ํฉํ ํํ๋ก ์ ๊ฐ
col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad) # ์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํ๊ธฐ ์ข์ ํํ๋ก ๋ณํ
col = col.reshape(-1, self.pool_h * self.pool_w) # ๊ฐ ํ๋ง ์์ญ์ ํ์ผ๋ก ๋ณํ
# ์ต๋๊ฐ ์ฐ์ฐ
out = np.max(col, axis=1) # ๊ฐ ํ๋ง ์๋์ฐ ๋ด์ ์ต๋๊ฐ์ ์ฐพ์
# ๊ฒฐ๊ณผ๋ฅผ ์ ์ ํ ๋ณํํ์ฌ ์ถ๋ ฅ ํํ๋ก ์กฐ์
out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
# transpose๋ฅผ ์ฌ์ฉํ์ฌ ์ถ์ ์์๋ฅผ ๋ณ๊ฒฝ (๋ฐฐ์น ํฌ๊ธฐ, ์ฑ๋ ์, ๋์ด, ๋๋น ์์ผ๋ก ์กฐ์ )
return out
- Pooling Layer ๊ตฌํ์ 3๋จ๊ณ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
- ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฐํ๋ค.
- ํ๋ ฌ ์ต๋๊ฐ์ ๊ตฌํ๋ค.
- ์ ์ ํ ๋ชจ์์ผ๋ก ์ฑํํ๋ค.
- ์ด๋ฌํ ๊ณผ์ ์ผ๋ก Pooling Layer์ forward ์ฒ๋ฆฌ๊ฐ ๊ตฌํ๋ฉ๋๋ค.
Convolution Neural Network (CNN) ๊ตฌํํ๊ธฐ
Convolutional Layer & Pooling Layer๋ฅผ ๊ตฌํํ์ผ๋, ์ด Layer๋ค์ ์กฐํฉํ์ฌ ์๊ธ์จ ์ซ์๋ฅผ ์ธ์ํ๋ CNN์ ํ๋ฒ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
- ์ด๋ฒ์๋ ์ด๋ฌํ ๊ตฌ์กฐ๋ก CNN์ ํ๋ฒ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
- ์ด CNN Network๋ "Convoultion-ReLU-Pooling-Affine-ReLU-Affine-Softmax" ์์ผ๋ก ํ๋ฆ ๋๋ค.
- ์ด๊ธฐํ๋์ ์ธ์๋ ๋ค์ ์ธ์๋ค์ ๋ฐ์ต๋๋ค.
- input_dim: ์ ๋ ฅ ๋ฐ์ดํฐ(์ฑ๋ ์, ๋์ด, ๋๋น)์ ์ฐจ์
- conv_param: ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ํ์ดํผํ๋ผ๋ฏธํฐ(๋์
๋๋ฆฌ)
- filter_num: ํํฐ ์
- filter_size: ํํฐ ํฌ๊ธฐ
- stride: ์คํธ๋ผ์ด๋
- pad: ํจ๋ฉ
- filter_num: ํํฐ ์
- hidden_size: ์๋์ธต(์์ ์ฐ๊ฒฐ)์ ๋ด๋ฐ ์
- output_size: ์ถ๋ ฅ์ธต(์์ ์ฐ๊ฒฐ)์ ๋ด๋ฐ ์
- weight_init_std: ์ด๊ธฐํ ๋์ ๊ฐ์ค์น ํ์คํธ์ฐจ
์ฌ๊ธฐ์ Convolutional Layer์ Hyperparameter๋ Dictionary ํํ๋ก ์ฃผ์ด์ง๋๋ค (conv_param).
- ์ด๊ฒ์ ํ์ํ Hyperparameter์ ๊ฐ์ด ['filter_num':30, 'filter_size':5, 'pad':0, 'stride': 1] ์ฒ๋ผ ์ ์ฅ๋๋ค๋ ๋ป์ ๋๋ค.
- ํ๋ฒ ์ฝ๋๋ฅผ ์ค๋ช ๋๋ฆด๊ฑด๋ฐ, ์ข ๊ธธ์ด๊ฐ ๊ธธ์ด์ 3๋ถ๋ถ์ผ๋ก ๋๋ ์ ์ค๋ช ํ๊ฒ ์ต๋๋ค.
class SimpleConvNet:
def __init__(self, input_dim=(1, 28, 28),
conv_param={'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
hidden_size=100, output_size=10, weight_init_std=0.01):
# ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ํ๋ผ๋ฏธํฐ
filter_num = conv_param['filter_num'] # ํํฐ์ ์
filter_size = conv_param['filter_size'] # ๊ฐ ํํฐ์ ํฌ๊ธฐ
filter_pad = conv_param['pad'] # ์ด๋ฏธ์ง ์ฃผ๋ณ์ ํจ๋ฉ
filter_stride = conv_param['stride'] # ํํฐ์ ์คํธ๋ผ์ด๋
input_size = input_dim[1] # ์
๋ ฅ ์ด๋ฏธ์ง ํฌ๊ธฐ (์ ์ฌ๊ฐํ์ด๋ผ๊ณ ๊ฐ์ )
# ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ์ถ๋ ฅ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐ
conv_output_size = (input_size - filter_size + 2 * filter_pad) / filter_stride + 1
conv_output_size = int(conv_output_size)
# ํ๋ง ๊ณ์ธต์ ๊ฐ์ ํ์ฌ 2x2 ํ๋ง ํฌ๊ธฐ์ ์คํธ๋ผ์ด๋ 2๋ฅผ ์ฌ์ฉ
# ์ด ๊ฒฝ์ฐ ๊ฐ ์ฐจ์์ ์ ๋ฐ์ผ๋ก ์ค์
pool_output_size = int(filter_num * (conv_output_size / 2) * (conv_output_size / 2))
# ๋คํธ์ํฌ ๊ฐ์ค์น ์ด๊ธฐํ
self.params = {}
- ์ด๊ธฐํ ์ธ์(_init_)์ผ๋ก ์ฃผ์ด์ง Convolutional Layer์ Parameter๋ฅผ Dictionary์์ ๊บผ๋ด๊ณ , ์ถ๋ ฅ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐํฉ๋๋ค.
self.params = {}
# ์ฒซ ๋ฒ์งธ ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ๊ฐ์ค์น ์ด๊ธฐํ
self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
# filter_num: ํํฐ์ ๊ฐ์, input_dim[0]: ์
๋ ฅ ์ฑ๋ ์ (์: ํ๋ฐฑ ์ด๋ฏธ์ง๋ 1, ์ปฌ๋ฌ๋ 3)
# filter_size: ํํฐ์ ๋์ด์ ๋๋น
# np.random.randn์ ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด๋ ๋์๋ฅผ ์์ฑ, weight_init_std๋ ์ด ๋์์ ํ์คํธ์ฐจ๋ฅผ ์กฐ์
self.params['b1'] = np.zeros(filter_num)
# ์ฒซ ๋ฒ์งธ ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ํธํฅ ์ด๊ธฐํ
# ํํฐ๋ง๋ค ํ๋์ ํธํฅ ๊ฐ์ ๊ฐ์ผ๋ฉฐ, ๋ชจ๋ ํธํฅ์ 0์ผ๋ก ์ด๊ธฐํ
# ๋ ๋ฒ์งธ ๊ณ์ธต (์์ ์ฐ๊ฒฐ ๊ณ์ธต)์ ๊ฐ์ค์น ์ด๊ธฐํ
self.params['W2'] = weight_init_std * np.random.randn(pool_output_size, hidden_size)
# pool_output_size: ํ๋ง ๊ณ์ธต ์ถ๋ ฅ์ ํฌ๊ธฐ (ํํฐ ์ * ๊ฐ์๋ ๋์ด * ๊ฐ์๋ ๋๋น)
# hidden_size: ์๋์ธต์ ๋ด๋ฐ ์
self.params['b2'] = np.zeros(hidden_size)
# ๋ ๋ฒ์งธ ๊ณ์ธต์ ํธํฅ ์ด๊ธฐํ
# ์๋์ธต์ ๋ด๋ฐ ์๋งํผ ํธํฅ์ 0์ผ๋ก ์ด๊ธฐํ
# ์ธ ๋ฒ์งธ ๊ณ์ธต (์ถ๋ ฅ ๊ณ์ธต)์ ๊ฐ์ค์น ์ด๊ธฐํ
self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)
# hidden_size: ์๋์ธต์ ๋ด๋ฐ ์
# output_size: ์ถ๋ ฅ์ธต์ ๋ด๋ฐ ์, ์ฆ ๋ถ๋ฅํ๊ณ ์ ํ๋ ํด๋์ค ์
self.params['b3'] = np.zeros(output_size)
# ์ธ ๋ฒ์งธ ๊ณ์ธต์ ํธํฅ ์ด๊ธฐํ
# ์ถ๋ ฅ์ธต์ ๋ด๋ฐ ์๋งํผ ํธํฅ์ 0์ผ๋ก ์ด๊ธฐํ
- ์์ ์ฝ๋๋ Weight Parameter(๊ฐ์ค์น ๋งค๊ฐ๋ณ์)๋ฅผ ์ด๊ธฐํ ํ๋ ๋ถ๋ถ์ ๋๋ค.
self.layers = OrderedDict()
# ์์๊ฐ ์ค์ํ ๊ณ์ธต๋ค์ ๊ด๋ฆฌํ๊ธฐ ์ํด OrderedDict์ ์ฌ์ฉํ์ฌ ๊ณ์ธต๋ค์ ์ ์ฅ
# ์ฒซ ๋ฒ์งธ ํฉ์ฑ๊ณฑ ๊ณ์ธต
self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
conv_param['stride'], conv_param['pad'])
# Convolution ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑ, ํํฐ ๊ฐ์ค์น์ ํธํฅ, ์คํธ๋ผ์ด๋, ํจ๋ฉ ์ ๋ณด๋ฅผ ์ ๋ฌ
# ์ฒซ ๋ฒ์งธ ํ์ฑํ ํจ์: ReLU
self.layers['Relu1'] = Relu()
# ReLU(Rectified Linear Unit) ํ์ฑํ ํจ์, ์์๋ฅผ 0์ผ๋ก ์ฒ๋ฆฌํ์ฌ ๋น์ ํ์ฑ ์ถ๊ฐ
# ์ฒซ ๋ฒ์งธ ํ๋ง ๊ณ์ธต
self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
# Pooling ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์์ฑ, 2x2 ํฌ๊ธฐ์ ํ๋ง ์๋์ฐ์ ์คํธ๋ผ์ด๋ 2๋ฅผ ์ค์
# ์ฒซ ๋ฒ์งธ ์์ ์ฐ๊ฒฐ ๊ณ์ธต
self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
# Affine ๊ณ์ธต (๋๋ ์์ ์ฐ๊ฒฐ ๊ณ์ธต), ๊ฐ์ค์น์ ํธํฅ์ ์ ๋ฌ
# ๋ ๋ฒ์งธ ํ์ฑํ ํจ์: ReLU
self.layers['Relu2'] = Relu()
# ๋ ๋ฒ์งธ ReLU ํ์ฑํ ํจ์ ์ธ์คํด์ค
# ๋ ๋ฒ์งธ ์์ ์ฐ๊ฒฐ ๊ณ์ธต
self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])
# ๋ ๋ฒ์งธ Affine ๊ณ์ธต, ์ถ๋ ฅ์ธต์ผ๋ก ์ฐ๊ฒฐ๋๊ธฐ ์ ์ ๋ง์ง๋ง ์๋์ธต
# ์์ค ๊ณ์ธต: Softmax-with-Loss
self.last_layer = SoftmaxWithLoss()
# SoftmaxWithLoss ํด๋์ค์ ์ธ์คํด์ค, ๋ถ๋ฅ ๋ฌธ์ ์์ ์ถ๋ ฅ์ธต์ ์์ค ํจ์ ๋ฐ ์ถ๋ ฅ ํ๋ฅ ์ ๊ณ์ฐ
- ์์๊ฐ ์๋ Dictionary (OrderedDict)์ธ Layer์ ๊ณ์ธต๋ค์ ์ฐจ๋ก๋ก ์ถ๊ฐํฉ๋๋ค.
- ๋ง์ง๋ง์ผ๋ก SoftmaxWithLoss ๊ณ์ธต ๋งํผ last_alyer๋ผ๋ ๋ณ๋ ๋ณ์์ ์ ์ฅํด๋ก๋๋ค.
- ์ด๋ ๊ฒ SimpleConvNet์ ์ด๊ธฐํ๋ฅผ ํ๊ณ , ์ถ๋ก ์ ์ํํ๋ Predict, Loss ํจ์์ ๊ฐ์ ๊ตฌํ๋ Loss Method๋ฅผ ๊ตฌํํด ๋ณด๊ฒ ์ต๋๋ค.
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
def loss(self, x, t):
# x: ์
๋ ฅ ๋ฐ์ดํฐ
# t: ์ ๋ต ๋ ์ด๋ธ
y = self.predict(x)
return self.last_layer.forward(y, t)
- ๊ทธ๋ฆฌ๊ณ Backpropagation(์ค์ฐจ์ญ์ ํ๋ฒ)์ผ๋ก Gradient(๊ธฐ์ธ๊ธฐ)๋ฅผ ๊ตฌํ๋ ๊ตฌํ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
def gradient(self, x, t):
"""
๊ฐ ์ธต์ ๊ธฐ์ธ๊ธฐ๋ฅผ ๋ด์ ์ฌ์ (dictionary) ๋ณ์
grads['W1'], grads['W2'], ... ๊ฐ ์ธต์ ๊ฐ์ค์น
grads['b1'], grads['b2'], ... ๊ฐ ์ธต์ ํธํฅ
"""
# ์์ ํ
self.loss(x, t)
# loss ๋ฉ์๋๋ฅผ ํตํด ์์ ํ๋ฅผ ์งํํ๊ณ , ์์ค์ ๊ณ์ฐํฉ๋๋ค. ์ด๋ ์ญ์ ํ์ ์์์ ์์ ์ฌ์ฉ๋ฉ๋๋ค.
# ์ญ์ ํ
dout = 1
dout = self.last_layer.backward(dout)
# ๋ง์ง๋ง ์์ค ๊ณ์ธต์์ ์์ํ์ฌ ๊ธฐ์ธ๊ธฐ๋ฅผ ๊ณ์ฐํฉ๋๋ค. ์ด ์ด๊ธฐ ๊ธฐ์ธ๊ธฐ๋ 1๋ก ์ค์ ๋ฉ๋๋ค.
layers = list(self.layers.values())
layers.reverse()
# ๋คํธ์ํฌ์ ๊ณ์ธต์ ์ญ์์ผ๋ก ์ ๋ ฌํฉ๋๋ค. ์ญ์ ํ๋ ์ถ๋ ฅ์ธต์์ ์
๋ ฅ์ธต ์์ผ๋ก ์งํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
for layer in layers:
dout = layer.backward(dout)
# ๊ฐ ๊ณ์ธต์ ๋ํด ์ญ์ ํ๋ฅผ ์์ฐจ์ ์ผ๋ก ์คํํฉ๋๋ค. ์ด ๊ณผ์ ์์ ๊ฐ ๊ณ์ธต์ ํ๋ผ๋ฏธํฐ์ ๋ํ ๊ธฐ์ธ๊ธฐ๊ฐ ๊ณ์ฐ๋ฉ๋๋ค.
# ๊ฒฐ๊ณผ ์ ์ฅ
grads = {}
grads['W1'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db
grads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
grads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
# ๊ณ์ฐ๋ ๊ธฐ์ธ๊ธฐ๋ฅผ grads ์ฌ์ ์ ์ ์ฅํฉ๋๋ค. ๊ฐ ๊ณ์ธต์ ๊ฐ์ค์น์ ํธํฅ์ ๋ํ ๊ธฐ์ธ๊ธฐ๋ฅผ ๊ฐ๊ฐ ์ ์ฅํฉ๋๋ค.
return grads
- Backpropagation(์ค์ฐจ์ญ์ ํ๋ฒ)์ผ๋ก Gradient(๊ธฐ์ธ๊ธฐ)๋ฅผ ๊ตฌํ๋ ๊ณผ์ ์์, Forward Propagation(์์ ํ) & Back Propagation(์ญ์ ํ)๋ฅผ ๋ฐ๋ณตํฉ๋๋ค.
- ๋ง์ง๋ง์ผ๋ก grads๋ผ๋ Dictionary ๋ณ์์ ๊ฐ Weight Parameter์ Gradient๋ฅผ ์ ์ฅํฉ๋๋ค.
- ์ด๊ฒ์ด SimpleConvNet์ ๊ตฌํ์ ๋๋ค.
CNN ์๊ฐํํ๊ธฐ
ํฉ์ฑ๊ณฑ ๊ณ์ธต์ ์๊ฐํ ํด์ CNN์ด ๋ฌด์์ ๋ณด๊ณ ์๋๊ฒ์ด ๋ฌด์์ธ์ง ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
- ์ด ์ฝ๋๋ ํ์ต ์ ๊ณผ ํ์ Weight(๊ฐ์ค์น)๋ฅผ ๋น๊ตํด ๋ณด๋ ์ฝ๋์ ๋๋ค.
- ๊ฒฐ๊ณผ๋ ์๋์ ์ฌ์ง์ ๋์ต๋๋ค.
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
from simple_convnet import SimpleConvNet
def filter_show(filters, nx=8, margin=3, scale=10):
"""
c.f. https://gist.github.com/aidiary/07d530d5e08011832b12#file-draw_weight-py
"""
FN, C, FH, FW = filters.shape
ny = int(np.ceil(FN / nx))
fig = plt.figure()
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
for i in range(FN):
ax = fig.add_subplot(ny, nx, i+1, xticks=[], yticks=[])
ax.imshow(filters[i, 0], cmap=plt.cm.gray_r, interpolation='nearest')
plt.show()
network = SimpleConvNet()
# ๋ฌด์์(๋๋ค) ์ด๊ธฐํ ํ์ ๊ฐ์ค์น
filter_show(network.params['W1'])
# ํ์ต๋ ๊ฐ์ค์น
network.load_params("params.pkl")
filter_show(network.params['W1'])
- Weight์ ์์๋ ์ค์์ด์ง๋ง, ์ด๋ฏธ์ง์์๋ ๊ฐ์ฅ ์์ ๊ฐ์ ๊ฒ์์(0), ๊ฐ์ฅํฐ ๊ฐ์(255)์ ํฐ์์ผ๋ก ์ ๊ทํ ํฉ๋๋ค.
- ํ์ต ์ ํํฐ๋ ๋ฌด์์๋ก ๊ท์น์ฑ์ด ์์ง๋ง ํ์ต์ ๋ง์น ํํฐ๋ ์ค๋ฌด๋ฌ, ๋ฉ์ด๋ฆฌ ๋ฑ ๊ท์น์ ๋๋๋ค.
- ์ด๋ฌํ ํํฐ๋ ์์ง(์์์ด ๋ฐ๋ ๊ฒฝ๊ณ), ๋ธ๋กญblob(๊ตญ์์ ์ผ๋ก ๋ฉ์ด๋ฆฌ์ง ์์ญ) ๋ฑ์ ์ธ์ํฉ๋๋ค.
- ์ถ๋ ฅ ์ด๋ฏธ์ง 1์ ์ธ๋ก ์์ง์ ํฐ ํฝ์ ์ด ๋ํ๋๊ณ , ์ถ๋ ฅ ์ด๋ฏธ์ง 2๋ ๊ฐ๋ก ์์ง์ ํฐ ํฝ์ ์ด ๋ง์ด ๋์ต๋๋ค.
- ์ด๊ฑด ํ์ต๋ filter 2๊ฐ๋ฅผ ์ ํํ์ฌ ์ ๋ ฅ ์ด๋ฏธ์ง์ ํฉ์ฑ๊ณฑ ์ฒ๋ฆฌ๋ฅผ ํ ๊ฒฐ๊ณผ์ ๋๋ค.
- 'filter 1'์ ์ธ๋ก ์์ง์, 'filter 2'๋ ๊ฐ๋ก ์์ง์ ๋ฐ์์ ํฉ๋๋ค.
- ์ด์ฒ๋ผ ํฉ์ฑ๊ณฑ ํํฐ๋ ์์ง์ ๋ธ๋กญ๋ฑ์ ์์์ ์ธ ์ ๋ณด๋ฅผ ์ถ์ถํ ์ ์์ต๋๋ค.
Layer ๊น์ด์ ๋ฐ๋ฅธ ์ถ์ถ ์ ๋ณด์ ๋ณํ
Layer(๊ณ์ธต)์ด ๋ ๊น์ด์ง์๋ก ์ถ์ถ๋๋ ์ ๋ณด (์ ํํ๋ ๊ฐํ๊ฒ ๋ฐ์ํ๋ ๋ด๋ฐ)์ ๋ ์ถ์ํ ๋๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
- 1๋ฒ์งธ ์ธต์ ํฉ์ฑ๊ณฑ ๊ณ์ธต๋ง์์๋ ์์ง๋ ๋ธ๋กญ ๋ฑ์ ์ ์์ค ์ ๋ณด๊ฐ ์ถ์ถ๋๊ณ ๊ณ์ธต์ด ๊น์ด์ง์๋ก ์ถ์ถ๋๋ ์ ๋ณด๋ ๋ ์ถ์ํ๋ฉ๋๋ค.
- (์์ง -> ํ ์ค์ฒ -> ์ฌ๋ฌผ์ ์ผ๋ถ ๋ฑ)
- ํฉ์ฑ๊ณฑ & Pooling ๊ณ์ธต์ ์ฌ๋ฌ๊ฒน ์๊ณ , ๋ง์ง๋ง์ผ๋ก Fully-Connected Layer(FC)๋ฅผ ๊ฑฐ์ณ ์ถ๋ ฅํ๋ ๊ตฌ์กฐ์ ๋๋ค.
- ์ฒซ๋ฒ์งธ ์ธต์ ์์ง & ๋ธ๋กญ, 3๋ฒ์งธ ์ธต์ ํ ์ค์ฒ, 5๋ฒ์งธ ์ธต์ ์ฌ๋ฌผ์ ์ผ๋ถ, ๋ง์ง๋ง Fully-Connected Layer(FC)๋ ์ฌ๋ฌผ์ Class์ ๋ด๋ฐ์ด ๋ฐ์ํฉ๋๋ค.
๋ฐ์ํ