# 7.1 전체 구조
"""
합성곱 신경망convolutional neural network, CNN
(합성곱 : 컨볼루션. 두 함수 중 하나를 반전, 이동시켜가며 나머지 함수와의 곱을 연이어 적분하는 것)
지금까지 본 신경망은 인접하는 계층의 모든 뉴런과 결합되어 있었다. 이를 완전연결fully-connected
라고 하며, 이를 Affine 계층이라는 이름으로 구현했다.
CNN에는 여기에 합성곱 계층과 풀링 계층이 추가된다.
Affine-ReLu가 Conv-ReLU-(Pooling)으로 바뀌며 출력에 가까운 층에서는 Affine-ReLU 구성을
사용할 수 있다. 출력 계층에서는 Affine-Softmax 조합을 사용한다.
"""
# 7.2 합성곱 계층
# 7.2.1 완전연결 계층의 문제점
"""
완전연결Affine 계층 : 인접하는 계층의 뉴런이 모두 연결되고 출력의 수는 임의로 정할 수 있다.
단점은 데이터의 형상이 무시된다.(가로, 세로, 채널로 구성된 3차원 데이터인 이미지를 1차원 데이터로
평탄화해줘야 한다.)
합성곱 계층은 형상을 유지하기 때문에 형상을 가진 데이터의 패턴을 이해할 수 있다.
CNN에서는 입출력 데이터를 특징 맵feature map이라고도 한다.
"""
# 7.2.2 합성곱 연산
"""
이미지 처리에서 말하는 필터 연산에 해당한다.
입력데이터 * 필터 → 출력 데이터
(4, 4) (3, 3) (2, 2)
1 2 3 0 2 0 1 15 16
0 1 2 3 0 1 2 6 15
3 0 1 2 1 0 2
2 3 0 1
필터의 윈도우를 일정 간격으로 이동해가며 입력 데이터에 적용한다. 입력과 필터의 원소를 곱한 후
총합을 구한다(단일 곱셈-누산fused multiply-add, FMA) 결과를 출력에 저장한다.
이 과정을 모든 장소에서 수행한다.
(합성곱은 교차상관과 비슷한 연산이다. 플리핑을 하면 합성곱, 아니면 교차상관. 딥러닝에서는 이를
잘 구분하지 않으며, 딥러닝 라이브러리에서는 플리핑하지 않거나 플리핑 여부를 인수로 받는다.)
CNN에서는 필터의 매개변수가 가중치에 해당하며, 편향 역시 존재한다.
입력데이터 * 필터 + 편향 → 출력데이터
(4, 4) (3, 3) (2, 2) (2, 2)
1 2 3 0 2 0 1 15 16 3 18 19
0 1 2 3 0 1 2 6 15 9 18
3 0 1 2 1 0 2
2 3 0 1
"""
# 7.2.3 패딩
"""
패딩 : 합성곱을 수행 전 입력 데이터 주변을 특정 값(0 등)으로 채우는 것.
주로 출력 크기 조정을 목적으로 사용한다. 출력 크기가 커지기 때문에 입력 데이터의 공간적 크기를
유지한 채로 다음 계층에 전달할 수 있다.
"""
# 7.2.4 스트라이드
"""
스트라이드stride : 필터를 적용하는 위치의 간격. 일반적으로는 1이지만
2로 하면 필터를 적용하는 윈도우가 두 칸씩 이동한다. 스트라이드를 키우면 출력 크기가 작아진다.
이 값들에는 아래의 관계가 있다.
OH = (H + 2P + FH)/S + 1
OW = (W + 2P + FW)/S + 1
입력 크기 : (H, W)
필터 크기 : (FH, FW)
출력 크기 : (OH, OW)
패딩 : P, 스트라이드 : S
최종 결과는 정수로 나와야 한다.
"""
# 7.2.5 3차원 데이터의 합성곱 연산
"""
이미지는 가로 세로 채널이 있는 3차원 데이터이다. 채널의 수 만큼 필터가 필요하다.
모든 채널의 필터는 같은 크기여야 한다.
"""
# 7.2.6 블록으로 생각하기
"""
데이터와 필터를 직육면체 블록이라고 생각하면 쉽다. 이를 (채널, 높이, 너비) 순서로 나타낸다.
입력 데이터 * 필터 → 출력 데이터
(C, H, W) (C, FH, FW) (1, OH, OW)
출력으로 다수의 채널을 내보내려면 필터를 여러 개(FN개) 사용하면 된다.
따라서 필터의 가중치 데이터는 4차원이 된다. 편향은 채널 하나에 값 하나씩으로 구성된다.(FN, 1, 1)
입력 데이터 * 필터 + 편향 → 출력 데이터
(C, H, W) (FN, C, FH, FW) (FN, 1, 1) (FN, OH, OW)
"""
# 7.2.7 배치 처리
"""
데이터 N개를 배치 처리한다면 데이터 형태는 다음과 같다.
입력 데이터(N개) * 필터(FN개) + 편향 → 출력 데이터(N개)
(N, C, H, W) (FN, C, FH, FW) (FN, 1, 1) (N, FN, OH, OW)
"""
# 7.3 풀링 계층
"""
풀링 : 가로 세로 방향의 공간을 줄이는 연산. 여러 영역을 원소 하나로 집약하여 공간 크기를 줄인다.
2*2 최대 풀링max pooling을 스트라이드 2로 처리하는 예
(2*2 영역에서 가장 큰 원소 하나를 꺼낸다.)
1 2 3 0 2 3
0 1 2 3
3 0 1 2 4 2
2 3 0 1
일반적으로 윈도우 크기와 스트라이드는 같은 값으로 설정한다.
최대 풀링 외에도 평균 풀링 등이 있다. 이미지 인식 분야에서는 주로 최대 풀링을 사용한다.
"""
# 7.3.1 풀링 계층의 특징
"""
학습해야 할 매개변수가 없다 : 최댓값이나 평균을 취하는 명확한 연산이다.
채널 수가 변하지 않는다 : 채널 독립적으로 계산한다.
입력의 변화에 영향을 적게 받는다(강건하다) : 입력데이터가 조금 변해도 풀링의 결과는 잘 변하지 않는다.
"""
# 7.4 합성곱/풀링 계층 구현하기
# 7.4.1 4차원 배열
"""
CNN의 데이터는 4차원이다.
"""
x = np.random.rand(10, 1, 28, 28) # 무작위 데이터 생성
print(x.shape)
# 첫 번째 데이터, 두 번째 데이터
print(x[0].shape) # (1, 28, 28)
print(x[1].shape) # (1, 28, 28)
# 첫 번째 데이터의 첫 채널의 공간 데이터
print(x[0, 0]) # or x[0][0]. 28 * 28의 벡터
# 7.4.2 im2cal로 데이터 전개하기
"""
합성곱을 구현하려면 다중 for문을 사용한다. 이는 복잡하고 numpy에서는 for문을 사용하면
성능이 떨어지기 때문에 im2col()이라는 함수를 사용해 구현한다.
im2col(image to column)은 필터를 적용하는 영역(3차원 블록)을 한 줄로 늘어놓는다.
4차원 데이터가 2차원이 된다. 여러 딥러닝 프레임워크에서 해당 함수를 구현해 사용하고 있다.
메모리를 더 많이 소비하는 단점이 있지만 행렬 계산 라이브러리는 큰 행렬의 계산에 장점이 있으므로
효율이 높아진다.
입력데이터를 2차원으로, 필터를 세로 1열로 전개해 내적을 취한 뒤 이를 4차원으로 reshape한다.
"""
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from simple_convnet import SimpleConvNet
from matplotlib.image import imread
from common.layers import Convolution
def filter_show(filters, nx=4, show_num=16):
"""
c.f. https://gist.github.com/aidiary/07d530d5e08011832b12#file-draw_weight-py
"""
FN, C, FH, FW = filters.shape
ny = int(np.ceil(show_num / 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(show_num):
ax = fig.add_subplot(4, 4, i+1, xticks=[], yticks=[])
ax.imshow(filters[i, 0], cmap=plt.cm.gray_r, interpolation='nearest')
network = SimpleConvNet(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)
# 학습된 가중치
network.load_params("params.pkl")
filter_show(network.params['W1'], 16)
img = imread('../dataset/lena_gray.png')
img = img.reshape(1, 1, *img.shape)
fig = plt.figure()
w_idx = 1
for i in range(16):
w = network.params['W1'][i]
b = 0 # network.params['b1'][i]
w = w.reshape(1, *w.shape)
#b = b.reshape(1, *b.shape)
conv_layer = Convolution(w, b)
out = conv_layer.forward(img)
out = out.reshape(out.shape[2], out.shape[3])
ax = fig.add_subplot(4, 4, i+1, xticks=[], yticks=[])
ax.imshow(out, cmap=plt.cm.gray_r, interpolation='nearest')
plt.show()
# coding: utf-8
import numpy as np
def smooth_curve(x):
"""손실 함수의 그래프를 매끄럽게 하기 위해 사용
참고:http://glowingpython.blogspot.jp/2012/02/convolution-with-numpy.html
"""
window_len = 11
s = np.r_[x[window_len-1:0:-1], x, x[-1:-window_len:-1]]
w = np.kaiser(window_len, 2)
y = np.convolve(w/w.sum(), s, mode='valid')
return y[5:len(y)-5]
def shuffle_dataset(x, t):
"""데이터셋을 뒤섞는다.
Parameters
----------
x : 훈련 데이터
t : 정답 레이블
Returns
-------
x, t : 뒤섞은 훈련 데이터와 정답 레이블
"""
permutation = np.random.permutation(x.shape[0])
x = x[permutation, :] if x.ndim == 2 else x[permutation, :, :, :]
t = t[permutation]
return x, t
def conv_output_size(input_size, filter_size, stride=1, pad=0):
return (input_size + 2*pad - filter_size) / stride + 1
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
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]
댓글 없음:
댓글 쓰기