CNN의 이해
코드는 ANN과 거의 비슷해서 달라진 부분만 설명하겠다.
ANN이 인간의 뇌를 모방한다면, CNN은 인간의 시신경을 모방한다.
CNN은 convolutional layer와 pooling layer가 있는 것이 특징이다.
convolutional layer는 필터를 이미지 위로 이동시켜가며 정보를 수집하고, 그 정보를 바탕으로 특징맵을 생성한다.
pooling layer는 생성된 특징맵의 크기를 줄인다. max pooling과 average pooling이 많이 사용된다.
max pooling은 필터 내에서 가장 큰 값을 선택하고 나머지는 버리는 방법
average pooling은 필터 내에서 평균값을 구해서 평균으로 저장하는 방법
[1, 3]
[2, 4]
이러한 2x2 크기의 필터가 있다고 할 때, max pooling은 네 값 중 가장 큰 4만 남기고 나머지는 버린다.
average pooling은 넷의 평균인 2.5로 저장한다.
어느쪽이든 크기는 1x1로 줄어든다.
코드
ANN에서 나온 코드와 거의 비슷하다. 다른 부분은 신경망을 정의하는 부분인 바로 여기
self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
첫번째 콘볼루션 레이어
Conv2d( 입력채널, 출력채널, 필터크기, 패딩)
입력 채널: 3 (CIFAR-10 이미지가 RGB이므로 채널이 3)
출력 채널: 32 (이 레이어를 통과한 후 32개의 필터(특징 맵)를 생성)
커널 크기: 3x3 (특징을 추출할 필터의 크기)
패딩: 1 (경계 처리를 통해 출력 크기를 유지)
-> 즉 3x3크기의 필터 32개가 이미지 위를 움직이면서 각각 다른 특징(엣지, 텍스처, 모서리..)을 잡아내고, 그 결과로 32개의 출력채널(특징맵)을 만든다.
CIFAR-10의 이미지는 32*32이고, RGB 이미지라서 채널=3 즉 초기 입력크기는 3x32x32
첫번째 콘볼루션 레이어 통과 후 생성된 출력값 32x32x32 (32개의 특징맵이 생기고 각 특징맵의 크기는 32x32)
self.pool = nn.MaxPool2d(2, 2)
풀링 레이어
MaxPool2d(풀링크기, 스트라이드)
이 풀링레이어는 max pooling 방식을 사용했다. 2x2 크기의 필터를 1x1 크기로 줄였기 때문에,
이 풀링레이어 통과 후 출력크기는 32*16*16
self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
두 번째 콘볼루션 레이어
입력 채널: 32 (첫번째 레이어와 풀링 후 32개를 받았으므로)
출력 채널: 64 (이 레이어를 통과한 후 64개의 필터(특징 맵)를 생성, 첫번째 레이어보다 더 많은 특징을 잡아낸다.)
커널 크기: 3x3 (특징을 추출할 필터의 크기)
이 레이어를 통과한 후 출력크기 64*16*16
x = x.view(-1, 64 * 8 * 8)
self.fc1 = nn.Linear(64 * 8 * 8, 512)
self.fc2 = nn.Linear(512, 10)
이 다음의 완전연결층 즉, self.fc1 = nn.Linear(64 * 8 * 8, 512) 코드로 들어가기 위해서는 1차원이 되어야 하므로 view를 이용해서 1차원 벡터로 펼쳐주고 있다. 이 과정을 플래튼(flatten)이라고 한다.
이후 64*8*8크기의 1차원 벡터가 512개의 노드로 변환되고
다시 512개의 노드를 받아 10개의 출력을 내어주고 있다. 이는 ANN의 모델에서도 이미 다뤘다.
64*8*8의 의미
self.conv2 = nn.Conv2d(32, 64, 3, padding=1) 을 통과한 후 출력크기는 분명 64*16*16이라고 되어있는데, 왜 갑자기 64*8*8이 되었을까?
정답은 바로 밑의 def forward(self,x): 에 있다.
x = self.pool(torch.relu(self.conv1(x)))
콘볼루션레이어1을 통과한 후(32*32*32) 풀링을 1회 시행(32*16*16)
x = self.pool(torch.relu(self.conv2(x)))
그 결과를 다시 콘볼루션레이어2에 통과시키고(64*16*16) 또 풀링을 1회 시행(64*8*8)
즉 총 2번의 풀링을 했으므로 최종 크기는 64*8*8
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = x.view(-1, 64 * 8 * 8)
x = torch.relu(self.fc1(x))
x = self.fc2(x)
코드 설명은 위에서 한 것과 ANN에서 한 부분과 동일하다
여기서 볼 것은 CNN 신경망이지만 결국 콘볼루션&풀링레이어에서 나온 값을 가져다가 최종적으로는 ANN으로 연결해서 결과를 낸다는 점이다.
CNN의 동작 원리 요약
- 컨볼루션 레이어는 필터를 이용해 이미지의 특징을 추출
- 풀링 레이어는 중요 특징을 유지하면서 크기를 줄임
- 여러 번의 컨볼루션과 풀링을 통해 이미지의 다양한 특징을 추출한 후, 이를 펼쳐서 완전 연결층으로 전달
- 최종적으로 각 클래스에 대한 예측 값을 출력