일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- tensorflow
- 데이터베이스
- MariaDB
- Tkinter
- 웹 프로그래밍
- 장고
- Docker
- Django
- yaml
- 컴퓨터 비전
- 파이토치
- Python
- pytorch
- kubernetes
- k8s
- POD
- 텐서플로우
- FLASK
- vue.js
- 딥러닝
- OpenCV
- Web Programming
- numpy
- Deep Learning
- paper review
- Computer Vision
- GUI
- 그래픽 유저 인터페이스
- 파이썬
- 논문 리뷰
- Today
- Total
Maxima's Lab
[Paper Review (논문 리뷰)] Aggregated Residual Transformations for Deep Neural Networks (RexNext) + 코드 구현 본문
[Paper Review (논문 리뷰)] Aggregated Residual Transformations for Deep Neural Networks (RexNext) + 코드 구현
Minima 2023. 3. 1. 11:43안녕하세요, 오늘은
Aggregated Residual Transformations for Deep Neural Networks
(https://arxiv.org/pdf/1611.05431.pdf)
위 논문에 대해서 리뷰를 해보고 해당 모델에 대해서 구현해보도록 하겠습니다.
RexNeXt는 ResNet의 성능을 개선하기 위해 ResNet과는 다른 방식으로 모델을 구성하였습니다. 이를 위해 ResNet에서 사용된 residual block을 확장하여, 여러 개의 경로를 사용하는 모델 아키텍처인 ResNeXt를 제안하였습니다.
RexNeXt의 핵심 아이디어는 Cardinality 입니다. Carninality는 ResNeXt에서 레이어 마다 입력 데이터를 여러 개의 경로로 보내는 개념을 나타냅니다. 예를 들어, Cardinality가 32라면, 각 레이어에서 입력 데이터를 32개의 서로 다른 경로로 보내고, 이들의 출력을 더하는 방식으로 모델을 구성합니다. 이를 통해 ResNet의 bottleneck 구조 내에서 다양한 크기의 필터를 사용하는 방법보다 더 효과적인 특성 추출이 가능해집니다.
논문에서는 ResNet, RexNext, Inception-v4 등 다양한 딥러닝 모델들을 비교하여 RexNeXt가 다른 모델들 보다 우수한 성능을 보인다는 것을 실험적으로 보여주었습니다. 또한, RexNeXt는 기존 딥러닝 모델들과 달리 모델 크기와 연산량을 증가시키지 않으면서도 성능을 향상 시킬 수 있었습니다.
다음은, ResNet Block과 ResNeXt Block (Cardinality=32) 구조에 대한 그림입니다.
이어서, ResNeXt Block을 구현하는 방법에 대해서 알아보겠습니다. (Pytorch)
먼저, ResNeXt Block을 구현한 코드입니다.
import torch
import torch.nn as nn
class ResNeXtBlock(nn.Module):
expansion = 2 # 추가
def __init__(self, in_channels, out_channels, cardinality, stride=1, downsample=None):
super(ResNeXtBlock, self).__init__()
mid_channels = cardinality * out_channels // 32
self.conv1 = nn.Conv2d(in_channels, mid_channels, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(mid_channels)
self.conv2 = nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=stride, padding=1, groups=cardinality, bias=False)
self.bn2 = nn.BatchNorm2d(mid_channels)
self.conv3 = nn.Conv2d(mid_channels, out_channels*self.expansion, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(out_channels*self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
입력 데이터 x를 identity에 저장합니다. 동시에, 입력 데이터 x를 첫번째 Convolution --> Bath Normalization --> ReLU 함수를 적용합니다. 이어서, ConVolution --> Batch Normalization --> ReLU 함수 적용의 반복과정을 진행합니다. 이때, 앞선 과정과 다른 점은 groups 파라미터에 Cardinality 값을 전달하여 그룹 내에서 채널을 나누어 처리하게 됩니다.
(groups는 Convolution에서 입력 채널을 몇 개의 그룹으로 나누어 처리할 지를 결정하는 파라미터입니다. 예를 들어, groups=1인 경우 입력 채널을 그룹으로 나누지 않고, 전체 입력 채널을 모두 사용하여 출력 채널을 계산합니다. groups=2인 경우, 입력 채널을 두 개의 그룹으로 나누어 처리하고 출력 채널을 계산합니다. 이와 같이, groups를 더 큰 값으로 설정하면 입력 채널을 더 많은 그룹으로 나누어 처리할 수 있습니다.)
세번째 convolution에서는 다시 출력 채널 수를 out_channels * self.expansion으로 늘려주고, Batch Normalization을 거쳐 최종적으로 출력은 계산하게 되며 필요 시 다운샘플링을 적용합니다. 이에 마지막으로, 계산된 출력과 identity를 더한 후 ReLU 함수를 적용하여 최종 출력으로 사용하게 됩니다.
아래는 ResNeXt 모델 전체 구조에 대한 코드 결과입니다.
class ResNeXt(nn.Module):
def __init__(self, block, layers, cardinality, num_classes=10):
super(ResNeXt, self).__init__()
self.in_channels = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, layers[0], cardinality)
self.layer2 = self._make_layer(block, 128, layers[1], cardinality, stride=2)
self.layer3 = self._make_layer(block, 256, layers[2], cardinality, stride=2)
self.layer4 = self._make_layer(block, 512, layers[3], cardinality, stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512 * block.expansion, num_classes)
def _make_layer(self, block, out_channels, blocks, cardinality, stride=1):
downsample = None
if stride != 1 or self.in_channels != out_channels * block.expansion:
downsample = nn.Sequential(
nn.Conv2d(self.in_channels, out_channels * block.expansion, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels * block.expansion)
)
layers = []
layers.append(block(self.in_channels, out_channels, cardinality, stride, downsample))
self.in_channels = out_channels * block.expansion
for i in range(1, blocks):
layers.append(block(self.in_channels, out_channels, cardinality))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
위의 코드들 중 멤버 변수와 멤버 함수에 대한 내용은 다음과 같습니다.
- 멤버 변수
- in_channels : 입력 데이터의 채널 수
- conv1, b1, relu, maxpool : 모델의 첫번째 레이어로, 각각 Conv2d, BatchNorm2d, ReLU, MaxPool2d 레이어로 구성
- layer1, layer2, layer3, layer4 : ResNeXt 블록으로 구성된 레이어
- avgpool : Global Average Pooling 레이어
- fc : Fully Connected 레이어로, 모델의 출력을 결정하는 레이어
- 멤버 함수
- _make_layer : ResNeXt 블록을 생성하는 함수
- forward L 모델의 순전파 연산을 수행하는 함수
지금까지,
Aggregated Residual Transformations for Deep Neural Networks
위 논문에 대해서 알아보고 Pytorch로 코드를 구현하는 방법에 대해 알아보았습니다.