합성곱 신경망 CNN
7.1 전체 구조
합성곱 계층, 풀링 계층
완전연결 - 인접하는 계층의 모든 뉴런과 결합 → Affine 계층
+ 합성곱 계층, 풀링 계층이 추가됨
출력에 가까운 층에서는 지금까지의 Affine-Relu 구성 사용 가능
마지막 출령 계층에서는 'Affine-Softmax' 조합 그대로 사용함
7.2 합성곱 계층
3차원 데이터같은 입체적인 데이터가 흐름
<완전연결 계층의 문제점>
데이터의 형상이 무시됨, 모든 입력 데이터를 동등한 뉴런으로 취급함 (형상에 담긴 정보를 살릴 수 없음)
3차원 데이터 → 1차원 데이터로 평탄화해서 입력
+ 합성곱 계층은 형상을 유지함
특징맵 - CNN에서 합성곱 계층의 입출력 데이터
입력데이터 - 입력 특징 맵 / 출력 데이터 - 출력 특징 맵
<합성곱 연산> 필터 연산
필터의 윈도우를 일정 간격 이동해가며 입력 데이터에 적용함
단일 곱셈-누산 = 입력과 필터에서 대응하는 원소끼리 곱한 후 그 총합을 구함, 결과를 출력의 해당 장소에 저장
15 = 2+0+3+0+1+4+3+0+2
필터의 매개변수 = 가중치
필터를 적용한 후의 데이터에 전해짐. 편향은 항상 (1X1) 하나만 존재
패딩 - 합성곱 연산을 수행하기 전에 입력 데이터 주변을 특정 값으로 채움
(4,4) → (6,6)
패딩 2,3 등 원하는 정수로 설정 가능
출력 크기를 조정할 목적으로 사용함
패딩의 폭을 1로 설정하니 출력이 같은 크기로 나옴. 공간적 크기를 고정한 채로 다음 계층에 전달 가능
스트라이드 - 필터를 적용하는 위치의 간격을 지정함
- 스트라이드를 키우면 출력 크기는 작아짐
- 패딩을 크게 하면 출력 크기는 커짐
<패딩, 스트라이드, 입력크기, 필터크기에 따른 출력 크기의 관계>
결과값이 정수로 나눠떨어지는 값이여야함
<3차원 데이터의 합성곱 연산>
길이 방향으로 특징 맵이 늘어남
채널 마다 입력데이터와 필터의 합성곱 연산을 수행. 결과를 더해서 하나의 출력을 얻음
입력 데이터의 채널 수와 필터의 채널 수가 같아야함 (ex. 3개)
필터 자체는 원하는 크기로 설정 가능 (모든 채널의 필터가 같은 크기여야함)
출력 데이터 한 장의 특징 맵 = 채널이 1개인 특징 맵
다수의 채널로 내보내기 위해 필터(가중치)를 다수 사용함!
필터 FN개 적용 → 출력 맵도 FN개 생성 (FN, OH, OW)
※ 필터의 수도 고려해야함 - 필터의 가중치 데이터는 4차원 데이터 (출력 채널 수, 입력 채널 수, 높이, 너비)
편향 - 채널 하나에 값 하나씩으로 구성됨
<배치 처리>
입력 데이터를 한 덩어리로 묶어서 배치로 처리함
계층을 흐르는 데이터의 차원을 하나 늘려서 4차원 데이터로 저장함 ▶ (데이터 수, 채널 수, 높이, 너비)
4차원 데이터가 하나 흐를 때마다 데이터 N개에 대한 합성곱 연산이 이뤄짐 ▶ N회 분의 처리를 한 번에 수행
<풀링 계층>
세로, 가로 방향의 공간을 줄이는 연산
최대 풀링 - 최댓값을 구하는 연산, 2X2 크기의 영역에서 가장 큰 원소 하나를 꺼냄
풀링의 윈도우 크기와 스트라이드는 같은 값으로 설정하는 것이 보통임
+) 평균 풀링 - 대상 영역의 평균을 계산함
풀링 계층의 특징
- 학습해야 할 매개변수가 없다.
- 채널 수가 변하지 않음 - 입력 데이터의 채널 수 그대로 출력 데이터로 내보냄 (채널마다 독립적으로 계산)
- 입력의 변화에 영향을 적게 받는다(강건하다) - 입력데이터가 가로로 1 원소만큼 어긋나도 출력이 같음(데이터에 따라 다를 수도 있음
7.4 합성곱/풀링 계층 구현하기
'트릭'을 사용해 쉽게 구현
im2cool 함수 - 입력 데이터를 필터링(가중치 계산)하기 좋게 전개하는 함수
3(4)차원 데이터에 im2cool 적용 → 2차원으로 변환
필터 적용 영역을 앞에서부터 순서대로 1줄로 펼침
필터 적용 영역이 겹치게 되면 im2cool로 전개한 원소 수 > 원래 블록 수
→ 메모리를 많이 소비하게 됨
필터를 세로로 1열로 전개 → im2cool이 전개한 데이터와 행렬 곱을 계산 → 출력 데이터를 변형함
2차원 출력 데이터 - 4차원으로 변형함
<합성곱 계층 구현>
im2col(input_data, filter_h, filter_w, stride=1, pad=0)
imput_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)
col1=im2col(x1, 5,5,stride=1, pad=0)
print(col1.shape) # (9,75)
x2=np.random.rand(10,3,7,7)
col2=im2col(x2, 5,5,stride=1, pad=0)
print(col2.shape) #(90,75)
배치 크기가 1일때 (9,75)
배치 크기가 10일때 (90,75)
Convolution 클래스 사용
class Convolution:
def __init__(self, W,b,stride=1, pad=0):
self.W=W
self.b=b
self.stride=stride
slef.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) / slef.stride)
col=im2col(x,FH,FW,self.stride, self,pad)
col_W=self.W.reshape(FN, -1).T
out=np.dot(col, col_W) + slef.b
out=out.reshape(N, out_h, out_w, -1).transpose(0,3,1,2)
return out
reshape을 사용해 2차원 배열로 전개
transpose() - 다차원 배열의 축 순서를 바꿔줌
<풀링 계층 구현>
#1. 입력 데이터를 전개한다.
col=im2col(x,self.pool_h, self.pool_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)
<CNN 구현하기>
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
pool_output_size = int(filter_num * (conv_output_size/2) *
(conv_output_size/2))
conv_param - 합성곱 계층의 하이퍼파라미터를 딕셔너리에 저장.
합성곱 계층의 출력 크기를 계산
self.params = {}
self.params['W1'] = weight_init_std * \
np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
self.params['b1'] = np.zeros(filter_num)
self.params['W2'] = weight_init_std * \
np.random.randn(pool_output_size, hidden_size)
self.params['b2'] = np.zeros(hidden_size)
self.params['W3'] = weight_init_std * \
np.random.randn(hidden_size, output_size)
self.params['b3'] = np.zeros(output_size)
가중치 매개변수를 초기화
params - 1번째 층 합성곱 계층과 나머지 두 완전연결의 가중치와 편향(매개변수)들을 딕셔너리에 저장
self.layers = OrderedDict()
self.layers['Conv1'] = Convolution(self.params['W1'],
self.params['b1'],
conv_param['stride'],
conv_param['pad'])
self.layers['Relu1'] = Relu()
self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
self.layers['Relu2'] = Relu()
self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])
self.last_layer = SoftmaxWithLoss()
layers - 순서가 있는 딕셔너리에 계층들을 차례로 추가
마지막 SoftmaxWithLoss 계층은 last_layer 별도 변수에 저장
def predict(self, x):
#추론 수행
for layer in self.layers.values():
x = layer.forward(x)
return x
def loss(self, x, t):
#손실함수 값
y = self.predict(x)
return self.last_layer.forward(y, t)
predict() - 추론을 수행함
loss() - 손실 함수의 값 구함
grads[] - 가중치에 대한 역전파 값 딕셔너리에 저장
7.6 CNN 시각화하기
학습 전 : 필터 무작위로 초기화, 흑백의 정도에 규칙성 x
학습 후 : 규칙성이 있는 이미지
에지 - 색상이 바뀐 경계선
블롭 - 국소적으로 덩어리진 영역
- 합성곱 계층이 깊어지면서 더 복잡하고 추상화된 정보가 추출됨.
- 단순한 에지에 반응 → 텍스처에 반응 → 복잡한 사물의 일부에 반응 (고급 정보로 변화해감, 사물의 '의미'를 이해)
7.7 대표적인 CNN
<LeNet>
손글씨 숫자를 인식하는 네트워크
합성곱 계층과 풀링 계층을 반복, 마지막에 완전연결 계층을 거치면서 결과를 출력
- 활성화 함수 - 시그모이드 함수 사용 (현재는 ReLu)
- 서브샘플링을 하여 중간 데이터의 크기를 줄임 (현재는 최대 풀링이 주류)
<AlexNet>
합성곱 계층과 풀링 계층을 반복, 마지막에 완전 연결 계층을 거치면서 결과를 출력
- 활성화 함수 - ReLu 사용
- LRN - 국소적 정규화를 실시하는 계층을 이용
- 드롭아웃 사용
+ GPU, 빅데이터가 딥러닝의 발전에 기여함
'AI > 딥러닝' 카테고리의 다른 글
[밑바닥부터 시작하는 딥러닝] 8장 딥러닝 (0) | 2023.04.29 |
---|---|
[밑바닥부터 시작하는 딥러닝] 6장 학습 관련 기술들 (1) | 2023.04.08 |
[밑바닥부터 시작하는 딥러닝] 5장 오차역전파법 (0) | 2023.04.01 |
[밑바닥부터 시작하는 딥러닝] 4장 신경망 학습 (0) | 2023.03.25 |
[밑바닥부터 시작하는 딥러닝] 3장 신경망 (0) | 2023.03.18 |