2-Stage Detector의 대표적인 모델인 YOLO에 대해 공부한 내용을 작성해 보겠습니다.
1. 특징
- 단일 신경망 구조로 구성이 단순하며 빠르다.
- 주변 정보까지 학습하여 이미지 전체를 처리하기 때문에 Background Error가 적다.
- 훈련 단계에서 보지 못한 새로운 이미지에 대해서도 정확도가 높다.
- 단, SOTA Detection model에 비해 mAP가 다소 떨어진다.
2. Unified Detection
Region Proposal, Feature Extraction, Classification, Bounding Box Regression을 단일 신경망으로 처리하는 과정

가정)
- grid : 4 x 4 (논문에서는 7 x 7)
- 한 grid cell이 예측하는 bbox: 2개
- class 개수: 20
x, y는 bbox의 정 중앙의 좌표를 나타내고
w, h는 bbox의 너비와 높이로 실제 값이 아닌 Input Image로 normalize 하여 0~1 사이의 값을 가짐
Pc는 박스 내부에 물체가 존재할 확률을 말한다.
하나의 bbox에 대해 5개의 output이 나오게 된다.
이런 과정이 두 번 반복되고 20개의 클래스 각각의 확률을 포함하여 하나의 grid에 (5x2) + 20 = 30개의 output이 생긴다.
결론적으로 하나의 Image에 대해 4 x 4 x 30의 output tensor가 만들어진다.

3. YOLO Architecture

YOLO는 CNN 구조의 모델로 이미지 특징을 추출하는 24개의 Convolution Layer와 Class 확률과 bbox의 좌표(Coordinates)를 예측하는 2개의 FC Layer로 구성했다.
* GoogleNet의 inception 대신 1x1 reduction Layer와 3x3 conv Layer를 결합하여 구성했다.
4. Training
- ImageNet 데이터 셋으로 YOLO의 앞단 20개의 컨볼루션 계층을 사전 훈련시킨다.
- 사전 훈련된 20개의 컨볼루션 계층 뒤에 4개의 컨볼루션 계층 및 2개의 전결합 계층을 추가한다.
- YOLO 신경망의 마지막 계층에는 선형 활성화 함수(linear activation function) 적용, 나머지 모든 계층에는 leaky ReLU 적용
- 과적합(overfitting)을 막기 위해 드롭아웃(dropout)과 data augmentation을 적용한다.
- 구조상 문제 해결을 위해 아래 3가지 개선안을 적용한다.
- localization loss와 classification loss 중 localization loss의 가중치를 증가시킨다.
- 객체가 없는 grid cell의 confidence loss보다 객체가 존재하는 grid cell의 confidence loss의 가중치를 증가시킨다.
- bounding box의 너비(widht)와 높이(height)에 square root를 취해준 값을 loss function으로 사용한다.
5. YOLOv8 Architecture

위의 그림이 VOLOv8 구조를 한눈에 잘 보이게 만들어둔 것 같다.
먼저 Detection을 기준으로 YOLOv8의 backbone과 head는 아래와 같이 구성되어 있다.
# YOLOv8.0n backbone
backbone:
# [from, repeats, module, args]
- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
- [-1, 3, C2f, [128, True]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 6, C2f, [256, True]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 6, C2f, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 3, C2f, [1024, True]]
- [-1, 1, SPPF, [1024, 5]] # 9
# YOLOv8.0n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 3, C2f, [512]] # 12
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 3, C2f, [256]] # 15 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 12], 1, Concat, [1]] # cat head P4
- [-1, 3, C2f, [512]] # 18 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 9], 1, Concat, [1]] # cat head P5
- [-1, 3, C2f, [1024]] # 21 (P5/32-large)
- [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5)
위의 backbone과 head에 사용되는 Conv module의 구성
class Conv(nn.Module):
"""Standard convolution with args(ch_in, ch_out, kernel, stride, padding, groups, dilation, activation)."""
default_act = nn.SiLU() # default activation
def __init__(self, c1, c2, k=1, s=1, p=None, g=1, d=1, act=True):
"""Initialize Conv layer with given arguments including activation."""
super().__init__()
self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p, d), groups=g, dilation=d, bias=False)
self.bn = nn.BatchNorm2d(c2)
self.act = self.default_act if act is True else act if isinstance(act, nn.Module) else nn.Identity()
def forward(self, x):
"""Apply convolution, batch normalization and activation to input tensor."""
return self.act(self.bn(self.conv(x)))
def forward_fuse(self, x):
"""Perform transposed convolution of 2D data."""
return self.act(self.conv(x))
head에서 Detection의 구성
class Detect(nn.Module):
"""YOLOv8 Detect head for detection models."""
dynamic = False # force grid reconstruction
export = False # export mode
shape = None
anchors = torch.empty(0) # init
strides = torch.empty(0) # init
def __init__(self, nc=80, ch=()): # detection layer
super().__init__()
self.nc = nc # number of classes
self.nl = len(ch) # number of detection layers
self.reg_max = 16 # DFL channels (ch[0] // 16 to scale 4/8/12/16/20 for n/s/m/l/x)
self.no = nc + self.reg_max * 4 # number of outputs per anchor
self.stride = torch.zeros(self.nl) # strides computed during build
c2, c3 = max((16, ch[0] // 4, self.reg_max * 4)), max(ch[0], self.nc) # channels
self.cv2 = nn.ModuleList(
nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3), nn.Conv2d(c2, 4 * self.reg_max, 1)) for x in ch)
self.cv3 = nn.ModuleList(nn.Sequential(Conv(x, c3, 3), Conv(c3, c3, 3), nn.Conv2d(c3, self.nc, 1)) for x in ch)
self.dfl = DFL(self.reg_max) if self.reg_max > 1 else nn.Identity()
def forward(self, x):
"""Concatenates and returns predicted bounding boxes and class probabilities."""
shape = x[0].shape # BCHW
for i in range(self.nl):
x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
if self.training:
return x
elif self.dynamic or self.shape != shape:
self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
self.shape = shape
x_cat = torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2)
if self.export and self.format in ('saved_model', 'pb', 'tflite', 'edgetpu', 'tfjs'): # avoid TF FlexSplitV ops
box = x_cat[:, :self.reg_max * 4]
cls = x_cat[:, self.reg_max * 4:]
else:
box, cls = x_cat.split((self.reg_max * 4, self.nc), 1)
dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides
y = torch.cat((dbox, cls.sigmoid()), 1)
return y if self.export else (y, x)
def bias_init(self):
"""Initialize Detect() biases, WARNING: requires stride availability."""
m = self # self.model[-1] # Detect() module
# cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1
# ncf = math.log(0.6 / (m.nc - 0.999999)) if cf is None else torch.log(cf / cf.sum()) # nominal class frequency
for a, b, s in zip(m.cv2, m.cv3, m.stride): # from
a[-1].bias.data[:] = 1.0 # box
b[-1].bias.data[:m.nc] = math.log(5 / m.nc / (640 / s) ** 2) # cls (.01 objects, 80 classes, 640 img)
6. YOLOv8 vs YOLOv5
- C3 module -> C2f module
- Backbone에서 첫 번째 Conv Layer를 6x6 -> 3x3
- Conv Layer 2개 삭제
- BottleNeck에서 첫 번째 Conv Layer를 1x1 -> 3x3
- 분리된 head(Classification, Regression) 사용
- Anchor Free Detection
약 한 달 전 최신 모델을 공부하고 적용해보려 YOLOv8으로 프로젝트를 진행했는데 며칠 전 2024년 2월 27일 YOLOv9이 출시 됐다...
'DL' 카테고리의 다른 글
| [논문리뷰]CLIP (Learning Transferable Visual Models From Natural Language Supervision) (0) | 2024.11.13 |
|---|---|
| Contrastive Learning (2) | 2024.11.12 |
| GAN(Generative Adversarial Networks) 리뷰 (1) | 2024.03.16 |
| VGG-16 리뷰 (0) | 2024.03.14 |
| ResNet(Residual Neural Network) 리뷰 (0) | 2024.03.12 |