(예시)ResNet 50 구현

2023. 7. 23. 17:00ResNet

♣ 라이브러리

 

♣ 하이퍼파라미터 정의

 

 

♣ Load CIFAR-10 데이터 다운로드, 전처리 및 데이터로더 생성

 

transform.Compose

 

transforms.Compose를 이용하여 방대한 양의 이미지를 한 번에 변형시킬 수 있음. 입력데이터를 모델에 넣기 전에 전처리 과정을 거칠 때 사용함. 

 

  1. transforms.ToTensor(): 이 변환은 입력 데이터(이미지 또는 기타 데이터 유형)를 PyTorch 텐서로 변환합니다. 이미지의 경우, numpy 배열이나 PIL 이미지로 표현된 이미지 데이터를 텐서로 변환합니다. 결과적으로 얻어지는 텐서는 값이 [0, 1] 범위에 있습니다. 이 변환은 픽셀 값을 [0, 1] 범위로 스케일링하기 때문에, 8비트 이미지의 경우 255로 나누는 연산을 수행합니다.
  2. transforms.Normalize(mean, std): 이 변환은 텐서 데이터를 정규화(normalize)합니다. 평균값과 표준편차값을 사용하여 텐서 데이터를 정규화합니다. mean과 std 인자는 두 개의 튜플로 전달됩니다. 이 코드에서 평균과 표준편차 값은 (0.5, 0.5, 0.5)로 설정되어 있습니다. 이는 세 개의 색상 채널(RGB)에 대해 각각 0.5의 평균값을 뺀 후, 결과를 0.5로 나누어 정규화합니다. 결과적으로, 텐서의 픽셀 값은 [-1, 1] 범위로 스케일링됩니다.

transforms.Compose를 사용하여 이러한 변환을 순서대로 적용함으로써 입력 데이터는 텐서로 변환되고 정규화되어 딥러닝 모델에 주입하기 적합한 형태가 됩니다. 딥러닝 모델은 입력 데이터를 텐서 형식으로 예상하고 정규화된 픽셀 값을 가져야하기 때문에 이러한 전처리가 필요함

 

 

CIFAR10

 

CIRAR10 데이터셋은 32*32 크기의 컬러 이미지 60000개로 구성되어 있으며 10개의 classes로 분류됨. training dataset 이미지가 50000개, test dataset 이미지가 10000개임. 

 

10개 classes = airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck

 

 

train, test 데이터 가져오기

 

datasets.CIFAR10(root="../Data/", train=True, transform=transform, target_transform=None, download=True): 이 부분은 CIFAR-10의 훈련 데이터셋을 다운로드하고 전처리된 형태로 불러옴

 

    • root: 데이터셋의 저장 경로를 지정합니다. 여기서는 "../Data/" 폴더에 저장합니다.
    • train=True: 훈련 데이터셋을 불러오기 위해 True로 설정합니다.
    • transform=transform: 데이터 전처리를 위해 앞서 정의한 transform 파이프라인을 적용합니다. 이는 데이터를 텐서로 변환하고 정규화하는 과정을 포함합니다.
    • target_transform=None: 레이블 변환에 대한 추가적인 처리를 하지 않으므로, None으로 설정합니다.
    • download=True: CIFAR-10 데이터셋이 로컬에 없을 경우, 인터넷에서 다운로드하여 저장합니다.
  1. datasets.CIFAR10(root="../Data/", train=False, transform=transform, target_transform=None, download=True): 이 부분은 CIFAR-10의 테스트 데이터셋을 다운로드하고 전처리된 형태로 불러옵니다. 매개변수들은 훈련 데이터셋과 동일하며, train=False로 설정하여 테스트 데이터셋을 불러옵니다.

 

 

PyTorch의 DataLoader를 사용하여 CIFAR-10 데이터셋을 미니배치로 나누고, 데이터를 불러오기

 

 

  1. DataLoader(cifar10_train, batch_size=batch_size, shuffle=True, num_workers=2, drop_last=True): 이 부분은 훈련 데이터셋인 cifar10_train을 DataLoader로 불러오는 과정을 나타냅니다.
    • cifar10_train: 앞서 전처리된 CIFAR-10 훈련 데이터셋을 가리킵니다.
    • batch_size: 미니배치의 크기를 지정합니다. 여기서는 batch_size 변수에 저장된 값으로 설정됩니다.
    • shuffle=True: 데이터를 에포크마다 섞을지 여부를 결정합니다. 훈련 데이터를 무작위로 섞어 다양한 데이터를 모델에 제공하여 학습을 더 효과적으로 만듭니다.
    • num_workers=2: 데이터를 불러오는 동안 사용할 워커(스레드)의 수를 지정합니다. 데이터를 병렬로 불러오기 위해 멀티스레딩을 사용합니다.
    • drop_last=True: 마지막 배치의 크기가 미니배치 크기에 미치지 못하는 경우 해당 배치를 삭제할지 여부를 결정합니다. True로 설정하면 마지막 배치가 미니배치 크기보다 작을 때 해당 배치를 삭제합니다.
  2. DataLoader(cifar10_test, batch_size=batch_size, shuffle=False, num_workers=2, drop_last=True): 이 부분은 테스트 데이터셋인 cifar10_test를 DataLoader로 불러오는 과정을 나타냅니다. 매개변수들은 훈련 데이터셋과 동일하지만, shuffle=False로 설정되어 데이터를 섞지 않고 순서대로 불러옵니다. 테스트 단계에서는 데이터를 섞을 필요가 없으며, 정확한 평가를 위해 순서대로 데이터를 사용합니다.

 

 

클래스 정의하기

 

 

 

♣ Basic Module 생성

 

 

def conv_block_1(in_dim, out_dim, activation, stride=1): 이 함수는 1x1 컨볼루션(convolution) 블록을 정의하는 부분입니다.

  • in_dim: 입력 채널 수를 나타냅니다.
  • out_dim: 출력 채널 수를 나타냅니다.
  • activation: 활성화 함수를 의미합니다. ReLU 등의 함수가 사용될 수 있습니다.
  • stride=1: 스트라이드(stride) 값을 설정합니다. 

위 구조에서는 입력 채널 수 in_dim과 출력 채널 수 out_dim을 갖는 1x1 컨볼루션 레이어를 생성하고, 배치 정규화(BatchNorm) 레이어를 적용하며, 이후에 활성화 함수를 적용함. 

 

 

이미 Conv2d에서 ReLU 함수를 사용하는데도 불구하고 conv_block_1에서 활성화함수 activation을 또 사용하는 이유

 

nn.Conv2d 이후에 더 많은 레이어를 추가하기 위해서입니다. nn.Conv2d 레이어 다음에 배치 정규화(BatchNorm)와 활성화 함수를 포함하는 nn.Sequential을 반환하여, 컨볼루션 레이어 이후에 추가적인 레이어를 연결할 수 있도록 합니다. 이로 인해 모델의 표현력이 향상되며, 더 복잡한 기능을 학습할 수 있게 됨.

 

 

kernel_size=1

 

kernel_size=1를 nn.Conv2d 레이어에 사용하면 1x1 컨볼루션을 의미합니다. 1x1 컨볼루션은 입력 데이터에 1x1 크기의 커널을 사용하는 특수한 유형의 컨볼루션 연산입니다. 이는 입력 데이터에 선형 변환을 적용하는 것과 같습니다.

1x1 컨볼루션을 신경망에서 사용하는 것은 다음과 같은 효과를 가져옵니다:

  1. 차원 축소: 1x1 컨볼루션은 입력 데이터의 채널 수를 줄이면서 공간적인 차원은 유지합니다. 이렇게 채널 수를 줄이는 것은 모델의 계산 복잡도를 줄이고 효율성을 향상시키는데 도움이 됩니다.
  2. 특성 퓨전: 1x1 컨볼루션은 특성 맵의 서로 다른 채널들 사이에서 정보를 결합하는데 사용됩니다. 이를 통해 모델은 특성들 사이의 복잡한 관계를 학습하고 네트워크의 표현 능력을 향상시킬 수 있습니다.
  3. 비선형성: 커널 크기가 1x1이지만, 1x1 컨볼루션은 여전히 학습 가능한 가중치를 포함하여 비선형성을 도입합니다. 이는 네트워크가 데이터의 복잡한 패턴과 관계를 파악할 수 있도록 해줍니다.
  4. 병목 레이어: ResNet과 같은 몇몇 아키텍처에서 1x1 컨볼루션은 큰 컨볼루션을 적용하기 전에 채널 수를 줄이는 병목 레이어로 사용됩니다. 이는 깊은 모델의 효율적인 학습에 도움이 됩니다.

종합적으로, nn.Conv2d 레이어에서 kernel_size=1을 사용하는 것은 모델의 채널 수를 조작하고 데이터의 복잡한 관계를 학습하기 위해 전략적으로 활용하는 강력한 아키텍처 선택 방법입니다. 이를 통해 모델의 성능과 효율성을 향상시킬 수 있습니다.

 

 

 

nn.BatchNrom2d(out_dim)

 

이 코드는 2차원 배치 정규화(Batch Normalization) 레이어를 의미합니다. nn.BatchNorm2d는 Convolutional Neural Network (CNN)에서 사용되는 배치 정규화 기법을 적용하는 데 사용되는 PyTorch의 클래스입니다.배치 정규화는 딥러닝 모델을 안정적으로 학습시키고 성능을 향상시키는 데 도움이 되는 기법 중 하나입니다. 이 기법은 각 미니배치의 입력을 정규화하여 학습 과정을 안정화시키고, 그래디언트 소실이나 폭주를 완화하여 학습을 원활하게 만듭니다.

 

nn.BatchNorm2d(out_dim)에서 out_dim은 출력 채널의 수를 나타냅니다. 이 레이어는 각 미니배치에서 출력 채널에 대해 평균과 분산을 계산하고, 이를 사용하여 입력을 정규화합니다. 이렇게 정규화된 데이터는 활성화 함수의 입력으로 사용되어 학습 과정에서 안정성을 제공하고, 더 빠르고 효과적인 학습을 가능하게 합니다.

 

 

 

def conv_block_e(in_dim, out_dim, activation, stride=1)

 

이 함수는 3x3 컨볼루션 블록을 정의하는 부분입니다.

  • in_dim: 입력 채널 수를 나타냅니다.
  • out_dim: 출력 채널 수를 나타냅니다.
  • activation: 활성화 함수를 의미합니다. ReLU 등의 함수가 사용될 수 있습니다.
  • stride=1: 스트라이드(stride) 값을 설정합니다. 기본값은 1로, 3x3 컨볼루션을 의미합니다.

 

 

♣ Bottleneck Module

 

BottleNeck Module은 신경망의 깊이를 효율적으로 늘리기 위해서 사용함. 세 가지 크기의 layer를 조합하여 구성

 

 

super(BottleNeck, self).__init__()

self.down = down

 

__init__ 메서드: 이 메서드는 BottleNeck 모듈의 구성 요소를 초기화합니다. 이때 down 파라미터는 해당 BottleNeck 블록에서 feature map의 크기가 줄어드는지를 결정합니다.

 

 

self.layer: 이 부분은 BottleNeck 블록의 핵심적인 부분으로, 두 가지 타입이 있습니다.

  • 만약 down 파라미터가 True인 경우, 입력 feature map의 크기를 줄이기 위해 먼저 1x1 Convolution을 가진 conv_block_1 레이어를 사용하여 stride=2로 다운샘플링을 합니다. 그 다음 3x3 Convolution을 가진 conv_block_3 레이어를 사용하고 마지막으로 다시 1x1 Convolution을 가진 conv_block_1 레이어를 통해 최종 출력 채널로 변환합니다.
  • down 파라미터가 False인 경우, 입력 feature map의 크기를 그대로 유지하기 위해 1x1 Convolution 레이어로 시작하고, 이어서 3x3 Convolution 레이어를 사용하고, 다시 1x1 Convolution 레이어를 통해 최종 출력 채널로 변환합니다.

 

self.dim_equalizer: 이 부분은 입력과 출력 feature map의 채널 수를 맞추기 위해 사용됩니다. 1x1 Convolution 레이어로 채널 수를 조정합니다.

 

forward 메서드: 이 메서드는 BottleNeck 블록의 순전파(forward) 과정을 정의합니다. 만약 down 파라미터가 True라면 입력 feature map을 다운샘플링하고, 그렇지 않은 경우는 그대로 유지합니다. 또한 해당 모듈에 입력으로 들어온 feature map과 모듈 내에서 계산된 feature map을 더해주어 정보가 흐를 수 있도록 합니다. 최종적으로 Bottleneck 블록의 출력을 반환합니다.

 

 

♣ Define ResNet-50

 

ResNet-50 구성

 

위의 ResNet-50 모델은 5개의 레이어로 구성되어 있으며, 각 레이어는 Bottleneck 블록으로 구성된 layer_2, layer_3, layer_4, layer_5와 일반적인 컨볼루션 레이어로 구성된 layer_1으로 나뉩니다.

 

Bottleneck 블록은 기본 ResNet 구조에서 사용되는 개선된 블록으로, 네트워크의 깊이를 증가시키면서도 더 적은 파라미터를 가지도록 도와줍니다.

이제 각 레이어의 구성을 살펴보겠습니다:

 

- self.layer_1: 이 레이어는 첫 번째 컨볼루션 블록으로, 7x7 크기의 커널을 사용하여 입력 이미지의 채널을 base_dim으로 변환하고, 그 다음 ReLU 활성화 함수를 적용하고, 3x3 크기의 MaxPooling을 적용하여 공간적인 크기를 줄입니다.

 

nn.Conv2d(3, base_dim, 7, 2, 3) 의 파라미터는 각각 (in_channels, out_channels, kernel_size, stride, padding)을 의미함

 

- self.layer_2: 이 레이어는 Bottleneck 블록으로 구성되어 있습니다. 입력 채널은 base_dim이고 출력 채널은 base_dim * 4입니다. 이 레이어는 세 개의 Bottleneck 블록을 순차적으로 쌓습니다.

 

BottleNeck(in_dim, mid_dim, out_dim, activation, down=False)의 파라미터

  1. in_dim: 이 파라미터는 Bottleneck 블록에 들어오는 입력 채널 수(또는 입력 특성 맵)를 나타냅니다.
  2. mid_dim: 이 파라미터는 첫 번째 1x1 컨볼루션 이후의 채널 수를 나타냅니다. 이는 Bottleneck 블록 내에서 가운데 부분에 해당하는 채널의 수입니다.
  3. out_dim: 이 파라미터는 Bottleneck 블록의 출력 채널 수(또는 출력 특성 맵)를 나타냅니다. 이는 마지막 1x1 컨볼루션 이후의 채널 수입니다.
  4. activation: 이 파라미터는 Bottleneck 블록에서 사용되는 활성화 함수를 지정합니다. 주어진 코드에서 self.activation은 nn.ReLU()로 설정되어 있으므로, Bottleneck 블록 내의 각 컨볼루션 출력에 ReLU 활성화 함수가 적용됩니다.
  5. down: 이는 불리언 파라미터로, Bottleneck 블록이 다운샘플링을 수행하는지 여부를 결정합니다. down=True인 경우, 블록은 첫 번째 1x1 컨볼루션에서 스트라이드(stride)를 2로 설정하여 특성 맵의 공간적인 차원을 줄입니다. down=False인 경우, 공간적인 차원은 유지되며 다운샘플링이 수행되지 않습니다.

- self.layer_3: 이 레이어는 이전 레이어에서의 출력 채널 수가 base_dim * 4이고, 출력 채널 수가 base_dim * 8인 Bottleneck 블록 세 개를 순차적으로 쌓습니다.

 

- self.layer_4: 이 레이어는 이전 레이어에서의 출력 채널 수가 base_dim * 8이고, 출력 채널 수가 base_dim * 16인 Bottleneck 블록 여섯 개를 순차적으로 쌓습니다.

 

- self.layer_5: 이 레이어는 이전 레이어에서의 출력 채널 수가 base_dim * 16이고, 출력 채널 수가 base_dim * 32인 Bottleneck 블록 세 개를 순차적으로 쌓습니다.

 

또한, 마지막에 평균 풀링(Average Pooling) 레이어와 완전 연결 레이어(self.fc_layer)를 사용하여 최종 출력을 수행합니다. 이 모델은 주어진 입력 이미지를 각 클래스에 대한 확률 분포로 변환하여 이미지 분류 작업을 수행합니다.

 

 

 

*ResNet-50의 레이어 내에서 블록의 수의 변화

 

네트워크의 공간적인 차원과 깊이를 효율적으로 조절하기 위함입니다. 이러한 설계 선택은 깊은 레이어에서 복잡한 특징을 캡처하고 다운샘플링을 통해 계산 부담을 줄이는 균형을 이루기 위해 이루어집니다.

 

ResNet과 같은 딥러닝 네트워크에서 특성 맵의 공간적인 차원(다운샘플링)을 줄이면 공간적인 정보와 더 세부적인 세부사항이 손실될 수 있습니다. 이러한 차원 축소를 보상하고 더 큰 수용 영역을 유지하기 위해 보통 네트워크의 깊은 레이어에서 채널 수(또는 깊이 또는 특성 차원)를 증가시킵니다.

채널 수를 증가시킴으로써 네트워크는 입력 데이터에서 더 다양하고 추상적인 특징을 캡처할 수 있습니다. 이러한 고수준의 특징은 복잡한 패턴과 물체를 인식하는 데 중요합니다. 깊은 레이어에서 채널 수를 증가시킴으로써 네트워크는 줄어든 공간적인 차원에서도 더 많은 문맥 정보를 보존하고 입력의 더 풍부한 표현을 만들 수 있습니다.

한편, 블록의 증가는 레이어 내에서 잔차 블록의 개수를 나타냅니다. ResNet과 같은 잔차 블록은 여러 개의 컨볼루션 레이어로 구성되어 있으며, 훈련 중에 그래디언트 흐름을 돕는 역할을 하여 매우 깊은 네트워크의 훈련을 가능하게 합니다.

 

요약하면,

  1. 채널 수 증가: 이는 다운샘플링 중에 손실되는 공간적인 정보를 보상하고 더 다양하고 추상적인 특징을 캡처하기 위해 수행됩니다.
  2. 블록의 증가: 이는 레이어 내에서 잔차 블록의 개수를 나타냅니다. 블록의 증가는 네트워크를 더 깊게 만들고, 그래디언트가 소멸하는 문제를 해결하여 매우 깊은 모델의 훈련을 돕습니다.

ResNet-50의 경우, 네트워크가 깊어짐에 따라 일반적으로 채널 수와 블록 수 모두 증가합니다. 더 많은 채널 수는 더 복잡한 패턴을 캡처하고 더 큰 수용 영역을 유지하는 데 도움이 되며, 더 많은 블록은 네트워크를 깊게 만들고 훈련 중에 그래디언트 흐름을 개선합니다.

따라서, 채널 수 증가와 블록 수 증가는 딥러닝 네트워크 설계의 두 가지 다른 측면으로, 각각 네트워크의 성능을 향상시키고 훈련 능력을 강화하는 데 고유한 목적을 가지고 있습니다.

 

 

그러면 왜 layer5에서는 블록 수가 감소하나요?

 

ResNet-50의 구현에서 블록 수는 1부터 4번 레이어까지 증가한 후 5번 레이어에서 감소합니다. 이러한 설계 선택은 복잡한 특징을 캡처하고 수용 영역을 유지하며 계산 복잡성을 줄이는 균형을 이루기 위해 이루어집니다.

ResNet-50에서 블록 수를 1부터 4번 레이어까지 증가시키는 이유:

  1. 1부터 4번 레이어에서 블록 증가: 초기 레이어(1부터 4번 레이어)에서는 특성 맵의 공간적인 차원이 다운샘플링에 의해 점차적으로 줄어듭니다. 공간적인 차원이 줄어들면 미세한 공간 정보와 지역적인 세부사항이 손실될 수 있습니다. 중요한 공간 정보를 보존하고 수용 영역(컨텍스트 정보)을 유지하기 위해 이러한 레이어에서 블록 수를 증가시키는 것이 유익합니다. 블록 수를 증가시킴으로써 네트워크는 입력 데이터로부터 더 다양하고 추상적인 특징을 캡처하며, 이는 복잡한 패턴을 인식하는 데 중요합니다.

ResNet-50에서 블록 수를 5번 레이어에서 감소시키는 이유:

  1. 5번 레이어에서 블록 감소: 5번 레이어에서는 이전 레이어에서 다운샘플링으로 인해 특성 맵의 공간적인 차원이 크게 줄어듭니다. 이 시점에서 특성 맵은 상대적으로 더 작은 공간 해상도를 가지게 됩니다. 네트워크가 이미 이전 레이어에서 복잡한 패턴과 고수준의 특징을 캡처했기 때문에 5번 레이어에서 블록 수를 많이 유지하는 것은 과적합이나 계산 복잡성 증가의 위험이 있습니다. 따라서 과적합을 방지하고 계산 복잡성을 줄이기 위해 5번 레이어에서는 블록 수를 감소시킵니다. 더 적은 블록을 사용하면 네트워크는 여전히 관련 정보를 캡처하고 패턴을 인식할 수 있으면서도 계산적으로 효율적입니다.

요약하면, 1부터 4번 레이어까지 블록 수를 증가시키고 5번 레이어에서 블록 수를 감소시키는 결정은 모델 복잡성과 계산적 효율성 사이의 균형을 이루기 위한 것입니다. 이러한 아키텍처 설계는 입력 데이터의 지역적인 세부사항과 복잡한 패턴 모두를 효과적으로 캡처하면서도 합리적인 계산 요구사항을 유지합니다. 이러한 설계는 다양한 컴퓨터 비전 작업에 효과적으로 사용되며, ResNet-50을 강력하고 널리 사용되는 딥러닝 모델로 만듭니다.

 

 

♣ Train

 

 

 

※ 참고 자료

1. ResNet 50 구현: https://velog.io/@euisuk-chung/%ED%8C%8C%EC%9D%B4%ED%86%A0%EC%B9%98-%ED%8C%8C%EC%9D%B4%ED%86%A0%EC%B9%98%EB%A1%9C-CNN-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EC%9E%90-ResNet%ED%8E%B8

 

[파이토치] 파이토치로 CNN 모델을 구현해보자! (ResNet편)

안녕하세요! 지난번 포스트인 VGGNet과 GoogleNet 이후로 오늘은 ResNet 관련 포스트입니다.

velog.io