합성곱 신경망 ( CNN, Convolutional Neural Network )
CNN, Convolutional Neural Network
CNN은 합성곱(Convolution) 연산을 사용하는 ANN의 한 종류다.
Convolution을 사용하면 3차원 데이터의 공간적 정보를 유지한 채 다음 레이어로 보낼 수 있다.
대표적인 CNN으로는 LeNet(1998)과 AlexNet(2012)이 있다.
VGG, GoogLeNet, ResNet 등은 층을 더 깊게 쌓은 CNN기반의 심층 신경망(DNN)이다.
CNN의 네트워크 구조
- Convolutional layer
- Pooling layer
FC layer의 문제점
Convolutional layer
위 합성곱의 정의에서 두 함수를 이미지, 필터라고 생각하면, 필터를 이동시켜가며 이미지와 곱한 결과를 적분(덧셈)해 나간다는 뜻이 된다.
* 여기서 수행하는 적분(덧셈)은 단일 곱셈-누산(fused multiply-add, FMA)이라 한다.
* 행렬에서 반전(reverse)에 대응하는 것은 플리핑(flipping)인 듯. 사실 flipping하면 합성곱 연산이고 안하면 교차상관 연산인데 딥러닝에서는 잘 구분하지 않는다. 안해도 합성곱으로 본다. flipping 여부를 인수로 받기도 함.
* 필터를 커널이라고 하기도 한다.
* 편향은 필터를 적용한 결과 데이터에 더해진다. 편향은 항상 (1x1)이며 이 값을 모든 원소에 더한다.
Padding
Stride
출력 크기 계산
이 때 OH, OW는 정수로 나누어 떨어지는 값이어야 하지만,
딥러닝 라이브러리에서는 가까운 정수로 반올림 하는 등 에러내지 않고 진행하는 경우도 있다.
수식이 직관적으로 이해가 안되면, (8x4)행렬에 (2x2)필터를 적용시켜본다.
이리저리 하다보면 필터가 몇 번 적용되느냐는 필터의 크기보다는 stride가 얼마이냐가 결정한다는 것을 알 수 있다.
필터를 위에서부터 적용했을 때 필터의 크기는 맨 마지막에 필터가 들어갈 수 있느냐에만 관여하게 된다.
만약 필터를 모든 입력데이터의 원소에 한 번씩만 적용해야 한다면 stride=필터의 크기가 된다.
3차원 데이터의 합성곱 연산
<출처 : CS231n>
*하나의 Channel은 하나의 2차원 배열(행렬)이라고 생각하면 편하다.
Input은 3개의 Channel을 가지고 있는데, 각각의 Channel 당 하나의 필터 Channel이 적용되었다.
즉, 하나의 입력 채널에는 하나의 필터 채널이 필요하다.
이는 입력 데이터의 채널 수와 필터의 채널 수가 일치해야 한다는 것을 의미한다.
Output Volumn은 (3, 3, 2)로, 첫번째 output 채널은 필터 W0을 적용한 결과이고 두번째 output 채널은 필터 W1을 적용한 결과이다.
각각의 필터 채널에서 연산한 값을 모두 더한 값이 output 채널이 된다. 필터 채널이 얼마가 있든, 결국 다 더해져 하나의 output 채널을 구성한다.
따라서 output 채널의 수는 필터 채널의 수와는 관련 없고, 필터가 몇 개 있느냐가 결정한다.
또한 각 필터 채널에서 연산한 값을 모두 더해야 하므로 모든 채널의 필터는 같은 크기여야 한다.
예를 들어, 필터가 하나만 있으면 output은 채널의 수가 1개다. 2차원 배열(행렬) 하나가 output이다.
여기서는 W0, W1 두 개 있으니 output 채널이 2다.
이를 수식으로 나타내면 다음과 같다.
여기서는 위 이미지와 달리 (Number(개수), Channel, Height, Width) 순서로 나타냈다.
이런식으로 왼쪽을 고차원, 오른쪽을 저차원으로 나타내야 생각하기 편하다.
(C, H, W) * (FN, C, FH, FW) → (FN, OH, OW)
편향을 추가한 수식은 다음과 같다.
편향은 각각의 output 필터에 적용되는거니까, output 채널만큼 있어야 한다.
하나의 편향 채널은 단일 값이며 이 값이 하나의 output 채널의 모든 원소에 더해진다.
(FN, OH, OW) + (FN, 1, 1) → (FN, OH, OW)
배치처리를 추가한 수식은 다음과 같다.
N개의 배치를 받게 되어도 가중치는 각각에 적용되는 거니까 필터와 편향은 변함없고, output 개수만 N개로 늘어난다.
(N, C, H, W) * (FN, C, FH, FW) → (N, FN, OH, OW) + (FN, 1, 1) → (N, FN, OH, OW)
* TensorFlow에서 사용하는 경우 순서를 조금 다르게 나타낸다.
(N, H, W, C) * (FH, FW, C, FN) → (N, OH, OW, FN) + (1, 1, FN) → (N, OH, OW, FN)
결국 CNN에서 각 계층을 타고 흐르는 데이터는 4차원 형상이다.
Pooling layer
2x2 max pooling을 적용한 이미지.
<출처 : wiki>
보통 풀링의 window size와 stride는 같은 값으로 설정해서 모든 원소가 한 번씩만 연산에 참여하도록 한다.
여기서는 window size가 2x2, stride는 2다.
Pooling layer는 다음과 같은 특징을 지닌다.
- 학습해야 할 매개변수가 없다.
- 채널 수가 변하지 않는다.
Conv layer에서는 각 필터 채널을 적용한 결과 채널들을 다 더해야 하나의 output 채널이 되지만, Pooling layer에서는 결과를 더하지 않는다. 결과 채널이 그대로 output 채널이 되기 때문에 채널 수가 그대로다. - 입력 데이터의 변화에 민감하게 반응하지 않는다.(Robustness)
pooling layer를 적용하는 목적이 여기에 있는데 내가 찾아내고자 하는 특징의 위치를 중요하게 여기기 보다는, input이 그 특징을 포함하고 있느냐 없느냐를 판단하도록 하기 위해서 주변에 있는 값들을 뭉뚱그려서 보겠다는거다.
CNN 구현
CNN에서 계층 사이에 전달되는 데이터는 4차원이라는 점에 유의한다. (N, C, H, W)
im2col (image to column)
Conv layer 구현
3차원(배치처리 시 4차원) 입력 데이터에 im2col을 사용하면, 3차원 필터 1개를 1회 적용하는 영역(입력 데이터의 일부인 3차원 영역)을 한 row로 하는 행렬로 변환한다.
```python
col_x = im2col(x, FH, FW, self.stride, self.pad)
```
변환된 행렬의
row 개수(Height)는 필터 적용 횟수 = 출력 맵의 2차원 크기 = OH * OW
column 개수(Width)는 C * FH * FW
같은 방식으로 필터도 행렬로 전개하는데, 이 때 im2col을 사용할 필요는 없고, 그냥 reshape 사용하면 된다.
입력 데이터와는 조금 다른게, 배치처리를 하지 않은 단일 3차원 입력 데이터에 대해서 필터 FN개를 적용하게 되므로 3차원 필터 1개를 row로 하는 행렬로 변환한다. 그래야 Transpose한 다음 입력 데이터를 변환한 행렬과 곱했을 때, 필터 적용 영역과 곱하게 된다. 그래서 reshape(FN, -1).T 형태로 전개한다.
변환된 행렬의
row 개수(Height)는 FN
column 개수(Width)는 C * FH * FW
연산을 위해서 필터를 변환한 행렬을 Transpose한 다음 둘을 곱하면
(OH * OW , C * FH * FW) * (C * FH * FW , FN) = (OH * OW , FN)
이고, 각각의 필터 적용 영역에 해당 필터를 곱하게 되어 합성곱 연산과 동일한 결과가 나온다.
여기에 편향을 더해준다. (OH * OW , FN) + (FN, )
편향은 데이터를 전개한 행렬에 더하게 되므로 (FN, 1, 1)이 아니라 1차원 배열이어야 브로드캐스트가 제대로 동작한다.
(FN, 1, 1)을 더하고 싶다면 4차원으로 reshape한 다음 더해야한다.
또한 grads['b']가 역전파로부터 구한 .db 값이므로 1차원 배열이라서, 이도 3차원으로 reshape해줘야 기울기 갱신 시 에러가 발생하지 않는다.
마지막으로 이를 reshape한다. (OH * OW , FN) → (FN, OH, OW)
배치처리했을 경우 앞에 N만 붙여주고 마지막에 4차원으로 reshape하면 된다.
(N * OH * OW , C * FH * FW) * (C * FH * FW , FN) = (N * OH * OW , FN)
(N * OH * OW , FN) + (FN, )
(N * OH * OW , FN) → (N , OH, OW, FN) → (N , FN, OH, OW)
reshape할 때 주의할 점은 바로 (N * OH * OW , FN) → (N , FN, OH, OW)하면 안되고
(N , OH, OW, FN)으로 reshape한 다음 차원의 순서를 변경해야 한다.
```python
result_2d.reshape(N, OH, OW, FN).transpose(3, 0, 1, 2)
```
사실 이런거 잘 몰라도, 그냥 im2col 호출하면 알아서 해주니까 입력 데이터에 im2col 사용하고,
필터에 reshape(FN, -1).T 사용한 다음 둘을 곱한 결과를 4차원으로 reshape().transpose()하면 끝이다.
역전파
역전파는 순전파에서 행렬곱을 사용했기 때문에 Affine layer의 역전파와 동일하게 처리하면 된다.
마지막에 col2im으로 행렬을 다시 4차원 데이터로 만들어 리턴한다.
Pooling layer 구현
```python
col = col.reshape(-1, self.pool_h*self.pool_w)
```
이렇게 하면 전개한 행렬에서 ``python np.max(col, axis=1)``를 사용하면 각 row별 최댓값을 반환받을 수 있다.
그리고 나서 reshape한 후 transpose해야한다.
```python
out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)
```
im2col을 사용하면 여러 채널의 데이터가 한 row에 들어오게 되는데,
그렇기 때문에 이를 reshape 했을 때 데이터가 채널 별로 모여있는게 아니라 채널 각각에 적용한 순서대로 모여있다.
그래서 일단 max를 각 row에 대해 구한 후 reshape하고, transpose로 채널 별로 정렬한다.
역전파
ReLU처럼 값을 통과시키느냐(최댓값), 안시키느냐(나머지)의 문제이기 때문에 ReLU layer의 역전파와 동일하게 처리하면 된다.
CNN 시각화
<AlexNet의 첫번째 필터를 이미지로 나타낸 사진, cs231n>
각각의 필터는 같은 입력을 받아 같은 연산을 거쳐 갱신되지만, 초깃값을 모두 다르게 설정해놓았기 때문에 갱신된 후 필터가 가지는 값은 서로 다르다.
만약 필터의 초깃값을 모두 동일하게 설정해놓았다면, 가중치가 똑같이 갱신되어 같은 모양이 나오게 된다.
이것이 필터의 초깃값을 무작위로, 서로 다르게 설정해야 하는 이유다.
첫번째 필터는 edge(색상이 바뀌는 경계)와 blob(국소적으로 덩어리진 영역)을 학습하여, 입력 데이터로부터 이를 잡아낸다.
무작위로 초기화 된 필터가, 학습했더니 이런 모양이 되는 것이다.
AlexNet의
1번째 Conv layer에서는 Edge와 Blob을 잡아내고,
3번째 Conv layer에서는 texture를,
5번째 Conv layer에서는 Object Parts를,
8번째 FC layer에서는 Object Classes를 잡아낸다.
이렇게 layer를 거듭할 수록 더 추상적인 정보를 잡아내게 된다.
즉, 뉴런이 반응하는 대상이 단순한 패턴에서 사물의 의미를 나타내는 고급정보로 변화한다.
층을 깊게 쌓을 수록 더 추상적인 정보를 추출할 수 있고, 이런 장점을 극대화하기 위해 층을 deep하게 쌓은게 딥러닝이다.
위 사진의 필터들은 color인 것도 있고 grayscale인 것도 있는데, 이는 AlexNet에서 입력 데이터를 두 개의 프로세싱 스트림으로 나눠 처리하기 때문이다. 하나는 high-frequency, 이게 grayscale이고 다른 하나가 low-frequency, 이게 color features.
이미지에서 말하는 frequency는 pixel 값의 변화량을 의미한다. 변화량은 색상이 주변에 비해서 얼마나 변했느냐라고 생각하면 된다. high-frequency filter는 pixel의 변화량이 큰 지점을 잡아내기 때문에 그림의 경계를 잡아내고, low-frequency filter는 변화량이 작은 지점을 잡아내는 거니까 그림에서 국소적으로 덩어리진 영역을 잡아낸다.
참고
- 밑바닥부터 시작하는 딥러닝
- http://cs231n.github.io/convolutional-networks/
'Machine Learning > Theory' 카테고리의 다른 글
Supervised, Unsupervised / Linear Regression (0) | 2017.04.15 |
---|---|
딥러닝 ( DL, Deep Learning ) / 심층 신경망 ( DNN ) (0) | 2017.03.29 |
인공신경망 ( ANN ) #6-3 최적화 : 오버피팅 방지( weight decay, droupout ) / 하이퍼파라미터 최적화 (2) | 2017.03.26 |
인공신경망 ( ANN ) #6-2 최적화 : 초기 가중치 설정, 기울기 소실( gradient vanishing ), 배치 정규화 ( batch normalization ) (0) | 2017.03.25 |
인공신경망 ( ANN ) #6-1 최적화 : 가중치 최적화 기법 (0) | 2017.03.24 |