CNN은 Convolutional Neural Network, 즉 합성곱 신경망이다. 어떻게 구성되는지 어떻게 발전했는지 알아보도록 하자
1. CNN
입력층 → 합성곱층 → 풀링층 → 완전 연결층 → 출력층의 구조로 되어 있다. 합성곱층과 풀링층을 거치면서 입력 이미지의 주요 특성 벡터를 추출한다. 그 다음 주요 특성 벡터들은 완전 연결층을 거치면서 1차원 벡터로 변환된다. 마지막으로 출력층에서 활성화 함수인 Softmax 함수를 사용하여 최종 결과가 출력이 된다.
CNN은 이미지나 영상을 처리하는 유용하다. 그 이유가 뭘까?
이미지 분석은 3x3 배열을 오른쪽과 같이 펼쳐서 각 픽셀에 가중치를 곱하여 은닉층으로 전달한다. 그러나 펼쳐서 분석할 시 데이터의 공간 정보가 손실될 수 밖에 없다. 따라서 이를 방지하기 위해 도입된 것이 바로 합성곱층이고 이미지의 공간 정보를 유지한 상태로 학습이 가능한 모델이 바로 CNN이다!
자세한 내용을 알아보기 전에 filtering, stride, padding이 무엇인지 알아보자
# Filtering
하나의 합성곱 계층에는 입력되는 이미지의 채녈 개수만큼 필터의 채널의 크기가 정해지며, 각 채널에 할당된 필터를 적용함으로써 합성곱 계층의 출력 이미지가 생성된다. 필터링을 하는 이유는 노이즈를 제거하기 위함이고 필터링을 진행하면 각 픽셀에 대해 가중평균 연산을 수행하는데 그 결과 이미지의 크기가 줄어듦을 알 수 있다.
# Padding
합성곱을 수행하게 되면 이미지의 크기가 입력 이미지의 크기보다 작아지게 된다. 이에 따라 가장자리에 위치한 정보는 점점 사라지게 된다. 이를 해결하기 위해서 사용하는 것이 padding이다. 패딩은 이미지으 가장자리에 특정 값(0, 상수...)으로 설정된 픽셀들을 추가함으로써 입력 이미지와 출력 이미지의 크기를 같거나 비슷하게 만드는 역할을 수행한다. CNN에서는 위 사진처럼 가장자리에 0을 추가하는 zero-padding이 자주 이용된다. 따라서 이처럼 패딩을 적용하면 출력 이미지의 크기가 입력 이미지의 크기와 같게 나온다.
# Stride
이미지에 대해 필터를 적용할 때 필터의 이동량을 의미하는 스트라이드를 설정해야한다. CNN을 구현할 때 합성곱 계층의 스트라이드는 주로 사진처럼 1로 설정된다.
그럼 각 층을 한번 잘 살펴보자
# 입력층
이미지나 영상이 입력되는 층이다. 이들은 Grayscale일 때는 1차원, 컬러인 경우는 3차원이 된다.
# 합성곱층
입력 데이터에서 특성을 추출한다. 이때 Kernel을 사용한다. 커널은 이미지의 모든 영역을 훑으면서 특성을 추출하게 되는데, 이렇게 추출한 결과물이 특성 맵이다. 커널 사이즈는 3x3, 5x5 크기가 일반적이며, 스트라이드라는 지정된 간격에 따라 순차적으로 이동한다.
# 풀링층
특성 맵의 차원을 다운 샘플링하여 연산량을 감소시키고, 주요한 특성 벡터를 추출하여 학습을 효과적으로 한다.
● 최대 풀링 : 대상 영역에서 최댓값으로 추출한다.
● 평균 풀링 : 대상 영역에서 평균을 반환한다.
# 완전 연결층
합성곱층과 풀링층을 거치면서 차원이 축소된 특성 맵은 최종적으로 완전연결층으로 전달된다. 이 과정에서 이미지는 3차원 벡터에서 1차원 벡터로 펼쳐지게 된다.
# 출력층
소프트맥스 함수가 사용되는데, 입력 받은 값을 0~1 사이의 값으로 출력한다. 따라서 마지막 출력층의 소프트맥스 함수를 사용하여 이미지가 각 레이블에 속할 확률 값이 출력되며, 이때 가장 높은 확률 값을 갖는 레이블이 최종 값으로 선정된다.
각 층에 대해 요약하면
CNN은 이미지의 특징을 추출하는 부분과 클래스를 분류하는 부분으로 나뉜다. 특징 추출 부분은 합성곱층과 풀링층을 여러 겹으로 쌓는 구조를 가진다. 합성곱층은 입력 데이터에 필터를 적용 후 활성화 함수를 반영하는 요소이다. 반대로 풀링층의 경우에는 선택적인 요소이다. 마지막에 나오는 완전 연결층은 이미지를 분류하는 층이다.
우리가 설정할 수 있는 파라미터는 무엇이 있을까?
● 합성곱 필터의 개수
보통 풀링층을 거치면 크기가 1/4로 줄어들기 때문에 합성곱층의 결과인 특성 맵의 개수를 4배정도 증가시키면 된다.
● 필터의 크기
작은 필터를 여러개 사용하면 특징을 더 돋보이게 하면서 연상량을 줄일 수 있다. 대부분 3x3 사이즈를 사용한다.
● 패딩 여부
패딩을 통해 입력 이미지의 크기가 줄어드는 것을 막을 수 있다.
● 스트라이드
이 값이 커지게 되면 데이터의 사이즈가 작아지게 된다.
● 풀링층의 종류
적당히 이미지 크기를 줄이며 특정 feature를 강조하는 역할을 한다.
최댓값을 뽑아내는 종류를 사용한다.
정리하자면...
CNN은 합성곱과 풀링을 반복하면서 특징을 추출하고, 추출한 특징을 완전연결층에 전달해 분류를 수행하는 것이다
2. CNN의 모델들
CNN 모델도 계속해서 발전을 하고 있다. AlexNet에서부터 VGG, GoogleNet, ResNet... 지금도 계속 발전 중이다. CNN은 네트워크의 depth를 늘리면서, 즉 layer 수를 늘려서 정확도를 올리는 방향으로 성능을 높이고 있다. 이때 성능을 높이기 위해서는 파라미터의 수를 줄여서 처리속도를 줄여야 한다.
다른 모델들도 있지만 ResNet을 중점적으로 보도록 하겠다
# ResNet
마이크로소프트에서 개발한 알고리즘으로 깊어진 신경망을 효과적으로 학습하기 위한 방법으로 residual 개념을 고안한 것이다. 일반적으로 신경망 깊이가 깊어질수록 딥러닝 성능은 좋아질 것 같지만, 실상은 그렇지 않다. 신경망은 깊이가 깊어질수록 성능이 좋아지다가 일정한 단계에 다다르면 오히려 성능이 나빠진다. ResNet은 이 문제를 해결하기 위해 레지듀얼 블록을 도입했다. 레지듀얼 블록은 기울기가 잘 전파될 수 있도록 일종의 숏컷을 만들어준다.
ResNet은 층이 총 152개로 구성되며 기울기 소멸 문제가 발생할 수 있기 때문에 숏컷을 가두어 기울기 소멸 문제를 방지한다.
합성곱층 하나를 블록으로 묶었다. 이렇게 묶인 계층들을 하나의 레지듀얼 블록이라고 한다. 이 레지듀얼 블록을 여러개 쌓은 것이 ResNet이라고 한다.
하지만 이렇게 계속해서 블록을 쌓으면 파라미터 수가 문제가 된다. 이를 해결하기 위해서 병목 블록을 둔다.
ResNet34와 ResNet50에서의 레지듀얼 블록을 한번 보자.
ResNet34는 기본 블록을 사용하며, ResNet50은 병목 블록을 사용한다. 기본 블록의 경우 파라미터 수가 39,2316M인 반면, 병목 블록의 경우 파라미터 수가 6.9623M이다. 깊이가 깊어졌음에도 파라미터 수는 감소했다.
=>1x1 합성곱층의 채널 수를 조절하면서 차원을 줄였다 늘리는 것이 가능하기 때문에 파라미터 수를 줄일 수 있다!
class Standard(nn.Module):
def __init__(self, in_dim=256, mid_dim=64, out_dim=64):
super(BuildingBlock, self).__init__()
self.building_block = nn.Sequential(
nn.Conv2d(in_channels=in_dim, out_channels=mid_dim, kernel_size=3, padding=1, bias=False),
nn.ReLU(),
nn.Conv2d(in_channels=mid_dim, out_channels=out_dim, kernel_size=3, padding=1, bias=False),
)
self.relu = nn.ReLU()
def forward(self, x):
fx = self.building_block(x)
out = fx + x
out = self.relu(out)
return out
#----------------------------------------------------------------------------------------------
# BottleNeck
class BottleNeck(nn.Module):
def __init__(self, in_dim=256, mid_dim=64, out_dim=256):
super(BottleNeck, self).__init__()
self.bottleneck = nn.Sequential(
nn.Conv2d(in_channels=in_dim, out_channels=mid_dim, kernel_size=1, bias=False),
nn.ReLU(),
nn.Conv2d(in_channels=mid_dim, out_channels=mid_dim, kernel_size=3, padding=1, bias=False),
nn.ReLU(),
nn.Conv2d(in_channels=mid_dim, out_channels=in_dim, kernel_size=1, bias=False),
)
self.relu = nn.ReLU()
def forward(self, x):
fx = self.bottleneck(x) # F(x)
out = fx + x # F(x) + x
out = self.relu(out)
return out
아이덴티티 매핑에 대해서 알아보자
기본 블록과 병목블록을 나타낸 사진에 보면 + 기호가 있다. 이 부분이 바로 아이덴티티 매핑이다. 이는 입력 x가 어떤 함수를 통과하더라도 다시 x라는 형태로 출력되도록 하는 것이다. forward() 코드를 한번 살펴보자.
def forward(self, x):
i = x
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.conv2(x)
x = self.bn2(x)
if self.downsample is not None :
i = self.downsample(i)
x += I
x = self.relu(x)
return x
입력 x를 i라는 변수에 저장했다. x는 합성곱층을 통과하다가 마지막 x에 I를 더해주었다. 예를 들어 x가 (28, 28, 64)라고 가정하자. x를 i에 저장했기 때문에 (28, 28, 64)가 된다. 그리고 합성곱층을 통과하면서 같은 형태를 더하기 때문에 최종 형태도 그대로 될 것이다.
다운 샘플링에 대해서도 알아보도록 하자
영역의 시작 지점에서는 채널 수가 128로 늘어났고, /2라는 것으로 보아 첫 번째 블록에서 합성곱층의 스트라이드가 2가 늘어나 (14,14,128)로 바뀐다.
위의 세 블록과 아래의 두 블록 간의 형태를 맞추지 않으면 아이덴티티 매핑을 할 수 없게 된다. 따라서 아이덴티티에 대해 다운샘플이 필요하다. 입력과 출력의 형태를 같도록 맞추어 주기 위해서 스트라이드 2를 가진 1x1 합성곱 계층을 하나 연결해 주면 된다. 이처럼 입력과 출력의 차원이 같은 것을 아이덴티티 블록이라고 하며, 입력 및 출력 차원이 동일하지 않고 입력의 차원을 출력에 맞추어 변경해야하는 것을 프로젝션 숏컷, 합성곱 블록이라고 한다.
다음 시간엔 시계열 분석에 대해 알아보자
'AI > DeepLearning' 카테고리의 다른 글
[DL] LSTM & GRU & 양방향 RNN (0) | 2024.11.15 |
---|---|
[DL] RNN (7) | 2024.11.13 |
[DL] 시계열 분석 (1) | 2024.11.08 |
[DL] 딥러닝의 구조 (3) | 2024.10.01 |
[DL] 딥러닝을 위한 준비 (3) | 2024.09.27 |