18. Yolo v1 모델 설계 (1) - 인공지능 고급(시각) 강의 예습

안상훈·2024년 6월 20일
0

인공지능-시각

목록 보기
19/54
post-thumbnail

개요

본 블로그 포스팅은 수도권 ICT 이노베이션 스퀘어에서 진행하는 인공지능 고급-시각 강의의 CNN알고리즘 강좌 커리큘럼 중 일부 항목에 대한 예습 자료입니다..


1. Yolo v1

Yolo v1(You Only Look Once: Unified, Real-Time Object Detectio)논문의 저자는 Joseph Redmon으로
객체 탐지 알고리즘 중 Real-time에 가까운 빠른 속도로 객체 탐지가 가능한 Yolo 시리즈의 주 연구자이기도 하다.

Yolo시리즈의 첫번째에 해당하는 Yolo v1는 기존의 다른 객체 탐지 알고리즘이
객체를 2-Stage Detector방식으로 탐지했다면 Yolo v1는 Detector의 주 Task인 Localization, Classification과정을 병렬로 처리하는 1-Stage Detector방법론을 제안
이를 통해 빠른 Object Detction을 수행하면서 높은 mAP(mean Average Precision)을 달성한데 의의가 있다.

2. Unified Detection

위 사진처럼 Yolo v1은 Localization, Classification Task를 병렬로 처리하며, 이 방식을 논문에서는 Unified Detection이라 부른다.
Yolo v1은 Unified Detection방법론으로 수행하고자 하는 Object Detection를 수행하는데 이때 이 작업에 대한 풀이를 Single Regression Ploblem(선형 회귀 문제)로 보고 문제를 풀어나간다.

문제 풀이의 과정은 아래와 같다.

1) 이미지 분할

입력 이미지를 [S×S][S \times S]개의 Grid Cell로 나누며, 각각의 Grid Cell은 바운딩 박스 예측, 클래스 확률 예측을 병렬로 수행한다.
(Yolo v1은 S=7S=7이다.)

2) 바운딩 박스 예측

각 Grid Cell은 BB 개의 바운딩 박스를 예측한다.
(Yolo v1은 B=2B = 2이다)
바운딩 박스가 에측하고자 하는 인자값은
B_Box의 좌표값(x,yx, y)
B_Box의 크기값(w,hw, h)
그리고 B_Box에 객체가 존재하는 확률(Confidence Score)
총 5가지 인자값을 예측한다.

이 CS(Confidence Score)의 수식은 아래와 같다.

CS=P(object)×IOUpredtruthCS = P(\text{object}) \times \text{IOU}_{\text{pred}}^{\text{truth}}

여기서 P(object)P(\text{object})는 객체가 존재하는 확률(0~1)을 의미하며,
IOUpredtruth\text{IOU}_{\text{pred}}^{\text{truth}}는 예측한 B_Box와 Ground Truth B_Box가 맞아 떨어지는 정도를 의미한다.

IOU에 대한 설명은 위 사진으로 표현 할 수 있으며, 딥러닝 모델을 통해 예측한 Predict B_Box과 데이터셋의 Annotation 항목으로 추출할 수 있는 객체의 Ground B_Box의 합집합 면적(Union Region)대비 차집합 면적(Intersection Region) 비율값이라 보면 된다.

3) 클래스 확률 예측

각 그리드셀에서
객체의 중심점이 존재하는 그리드 셀
에 대하여 데이터셋의 클래스 라벨 정보(CC)와 비교해 해당 객체의 종류를 예측하는 작업을 수행한다.

이 객체의 중심점이 존재하는 그리드 셀 이라는 부분이 조금 어려움이 있는데

클래스 확률 예측 과정을 그림으로 표현하면 아래와 같다.

위 사진처럼 객체가 존재하는 모든 Grid Cell이 클래스 예측 과정에 참여하는 것이 아니라 Center Point가 있는 특정 Grid Cell만 클래스 확률 예측 과정에 참여하여 검출된 객체의 종류를 예측한다.

따라서 Yolo v1의 Classification 과정은 객체의 몸통부분만 보고 해당 객체의 종류를 판별한다 보면 된다.

4) 병렬처리 + 선형 회귀문제

위 "2) 바운딩 박스 예측", "3) 클래스 확률 예측" 과정을 수행하면서 발생하는 예측된 인자(Output)를 하나의 매트릭스로 처리해 Loss를 구하기에 두개의 회귀 문제가 병렬로 풀이된다.

이것을 그림으로 표현하면 위와 같으며
[S,S,(B5+C)][S, S, (B*5+C)]의 3차원 매트릭스로 Yolo v1이 예측 결과물을 출력하고
1개의 Gird Cell에 대해 인자값의 구성을 살펴보면
[BBox 1에 대한 인자값, BBox2에 대한 인자값, Class_Prob]
의 구성으로 이 값이 Loss Function에 입력되니
이를 '병렬로 처리한다 = 1-Stage Detector' 라고 보는 것이다.


3. Yolo v1 DarkNet

Yolo v1에 사용되는 Backbone 네트워크를 저자는 DarkNet이라 부르고 있으며,
해당 네트워크의 아키텍쳐 구조는 아래와 같다.

아키텍쳐의 구조를 본다면 입력 Img의 차원은 [448x448x3],

6개로 구분할 수 있는 Conv block와 이와 연결된
Dense Block으로 간단히 표현할 수 있으며,

마지막 Dense Block에서 출력되는 1차원 벡터는
차원변환을 수행하여 [S, S, (B*5+C)]의 3차원 Tensor구조로 변화시킨다.

이 S, S, B, C의 계수는 조정이 가능한 값이나

코드 리뷰까지 같이 수행하는 시점에서는

Yolo v1 논문이 작성될 시점에 사용된
데이터셋이 Pascal VOC \rightarrow 클래스 종류가 20(C)

이미지의 분할 개수는 7(S)

예측하는 B_Box의 개수는 2(B)를 선택하여
[7x7x30] 구조를 갖는 3차원 Tensor데이터가 출력되는 것이다.

여기에 추가로 Yolo 계열의 타 코드 리뷰를 본다면 DarkNet이 아닌 VGG계열의 CNN을 Backbone으로 사용하는 경우도 왕왕 존재한다.

사실 어떤 Backbone 네트워크를 사용하는 것이 중요하다 라기 보다는
Yolo v1의 API를 코드로 구현하는데는

[입력 처리] \rightarrow [백본 모델정의] \rightarrow [출력 처리]

이 3단계로 간단히 표현했을 때
[입력 처리], [출력 처리]이게 더 중요하다...

논문이 작성되던 시점에서는 DarkNetYolo v1Backbone네트워크로 사용하기에 해당 코드를 구현하고자 한다.

import torch
import torch.nn as nn
class YoloConv(nn.Module):
    def __init__(self, in_channels, out_channels, **kwargs):
        super(YoloConv, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)
        self.bachnorm = nn.BatchNorm2d(out_channels)
        self.relu = nn.LeakyReLU(negative_slope=0.1)

    def forward(self, x):
        x = self.conv(x)
        x = self.bachnorm(x)
        x = self.relu(x)
        return x
class Yolov1(nn.Module):
    def __init__(self, S=7, B=2, num_cleasses=20):
        super(Yolov1, self).__init__()

        #Grid cell 사이즈 : S, Boundbox 개수 : b
        self.S = S
        self.B = B
        self.C = num_cleasses
        #논문애서는 클래스를 'C'계수로 씀

        self.conv1 = nn.Sequential(
            YoloConv(3, 64, kernel_size=7, stride=2, padding=3),
            nn.MaxPool2d(kernel_size=2),
        )

        self.conv2 = nn.Sequential(
            YoloConv(64, 192, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )

        self.conv3 = nn.Sequential(
            YoloConv(192, 128, kernel_size=1, stride=1),
            YoloConv(128, 256, kernel_size=3, stride=1, padding=1),
            YoloConv(256, 256, kernel_size=1, stride=1),
            YoloConv(256, 512, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )

        self.conv4 = nn.Sequential(
            self._make_layer(in_ch=512, out_ch=256, blocks=4),
            YoloConv(512, 512, kernel_size=1, stride=1),
            YoloConv(512, 1024, kernel_size=3, stride=1, padding=1),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )

        self.conv5 = nn.Sequential(
            self._make_layer(in_ch=1024, out_ch=512, blocks=2),
            YoloConv(1024, 1024, kernel_size=3, stride=1, padding=1),
            YoloConv(1024, 1024, kernel_size=3, stride=2, padding=1),
        )

        self.conv6 = nn.Sequential(
            YoloConv(1024, 1024, kernel_size=3, stride=1, padding=1),
            YoloConv(1024, 1024, kernel_size=3, stride=1, padding=1),
        )

        self.fc_layer = nn.Sequential(
            nn.Flatten(),
            nn.Linear(1024 * self.S * self.S, 4096),
            nn.LeakyReLU(0.1),
            nn.Linear(4096, self.S * self.S * (self.B * 5 + self.C)),
        )

    #레이어가 반복되는 부분은 아래의 함수로 간단히 설계한다.
    def _make_layer(self, in_ch, out_ch, blocks):
        layers = []
        for _ in range(blocks):
            layers.append(YoloConv(in_ch, out_ch, kernel_size=1, stride=1))
            layers.append(YoloConv(out_ch, in_ch, kernel_size=3, stride=1, padding=1))
        return nn.Sequential(*layers)
    

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.conv5(x)
        x = self.conv6(x)

        x = self.fc_layer(x)
        x = x.view(-1, self.S, self.S, self.B * 5 + self.C)
        #마지막 레이어인 [N, 7, 7, 30]을 만드는부분
        return x

코드의 구성은 평이한
Conv block \rightarrow Dense Block로 넘어가는 CNN 네트워크이나

레이어를 만드는데 중요한 Tech으로
_make_layer를 사용했다.

    def _make_layer(self, in_ch, out_ch, blocks):
        layers = []
        for _ in range(blocks):
            layers.append(YoloConv(in_ch, out_ch, kernel_size=1, stride=1))
            layers.append(YoloConv(out_ch, in_ch, kernel_size=3, stride=1, padding=1))
        return nn.Sequential(*layers)

이 메서드 함수는 클래스 내부에서만 사용되며
해당 클래스의 기능은
이 사진에 표기된 Conv4, 5의 1,2번 레이어 반복 문구를 수행하는 함수다 라고 보면 된다.

이 함수명 앞에 _언더스코어 하나 붙이는거는
해당 영상을 보면 이해가 될 것이다.

딥러닝은 모델의 구조를 이해하는 것도 중요하지만

파이써닉(Pythonic) 하면서 객체지향적으로 코드를 구현해야 하니 다양한 문법에 대해 익혀둘 필요가 있다.

왜 이야기를 하냐면

다음 챕터에는
Yolo v1 Loss 함수 설계
데이터셋 전처리
평가지표 코드화
API를 활용한 이미지 추론작업

을 수행할 텐데

여기서 참 골아픈 코딩기법이 많이 등장하여
미리 적는것이다.

진짜 골아픈 내용이 많이 나와서 그렇다...

참고로 위 모델 설계 후 Summary를 하면 아래와 같이 출력된다.

from torchsummary import summary #설계한 모델의 요약본 출력 모듈

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

model = Yolov1()
model.to(device) #모델을 GPU로
summary(model, input_size=(3, 448, 448), device=device.type)
----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1         [-1, 64, 224, 224]           9,408
       BatchNorm2d-2         [-1, 64, 224, 224]             128
         LeakyReLU-3         [-1, 64, 224, 224]               0
          YoloConv-4         [-1, 64, 224, 224]               0
         MaxPool2d-5         [-1, 64, 112, 112]               0
            Conv2d-6        [-1, 192, 112, 112]         110,592
       BatchNorm2d-7        [-1, 192, 112, 112]             384
         LeakyReLU-8        [-1, 192, 112, 112]               0
          YoloConv-9        [-1, 192, 112, 112]               0
        MaxPool2d-10          [-1, 192, 56, 56]               0
           Conv2d-11          [-1, 128, 56, 56]          24,576
      BatchNorm2d-12          [-1, 128, 56, 56]             256
        LeakyReLU-13          [-1, 128, 56, 56]               0
         YoloConv-14          [-1, 128, 56, 56]               0
           Conv2d-15          [-1, 256, 56, 56]         294,912
      BatchNorm2d-16          [-1, 256, 56, 56]             512
        LeakyReLU-17          [-1, 256, 56, 56]               0
         YoloConv-18          [-1, 256, 56, 56]               0
           Conv2d-19          [-1, 256, 56, 56]          65,536
      BatchNorm2d-20          [-1, 256, 56, 56]             512
        LeakyReLU-21          [-1, 256, 56, 56]               0
         YoloConv-22          [-1, 256, 56, 56]               0
           Conv2d-23          [-1, 512, 56, 56]       1,179,648
      BatchNorm2d-24          [-1, 512, 56, 56]           1,024
        LeakyReLU-25          [-1, 512, 56, 56]               0
         YoloConv-26          [-1, 512, 56, 56]               0
        MaxPool2d-27          [-1, 512, 28, 28]               0
           Conv2d-28          [-1, 256, 28, 28]         131,072
      BatchNorm2d-29          [-1, 256, 28, 28]             512
        LeakyReLU-30          [-1, 256, 28, 28]               0
         YoloConv-31          [-1, 256, 28, 28]               0
           Conv2d-32          [-1, 512, 28, 28]       1,179,648
      BatchNorm2d-33          [-1, 512, 28, 28]           1,024
        LeakyReLU-34          [-1, 512, 28, 28]               0
         YoloConv-35          [-1, 512, 28, 28]               0
           Conv2d-36          [-1, 256, 28, 28]         131,072
      BatchNorm2d-37          [-1, 256, 28, 28]             512
        LeakyReLU-38          [-1, 256, 28, 28]               0
         YoloConv-39          [-1, 256, 28, 28]               0
           Conv2d-40          [-1, 512, 28, 28]       1,179,648
      BatchNorm2d-41          [-1, 512, 28, 28]           1,024
        LeakyReLU-42          [-1, 512, 28, 28]               0
         YoloConv-43          [-1, 512, 28, 28]               0
           Conv2d-44          [-1, 256, 28, 28]         131,072
      BatchNorm2d-45          [-1, 256, 28, 28]             512
        LeakyReLU-46          [-1, 256, 28, 28]               0
         YoloConv-47          [-1, 256, 28, 28]               0
           Conv2d-48          [-1, 512, 28, 28]       1,179,648
      BatchNorm2d-49          [-1, 512, 28, 28]           1,024
        LeakyReLU-50          [-1, 512, 28, 28]               0
         YoloConv-51          [-1, 512, 28, 28]               0
           Conv2d-52          [-1, 256, 28, 28]         131,072
      BatchNorm2d-53          [-1, 256, 28, 28]             512
        LeakyReLU-54          [-1, 256, 28, 28]               0
         YoloConv-55          [-1, 256, 28, 28]               0
           Conv2d-56          [-1, 512, 28, 28]       1,179,648
      BatchNorm2d-57          [-1, 512, 28, 28]           1,024
        LeakyReLU-58          [-1, 512, 28, 28]               0
         YoloConv-59          [-1, 512, 28, 28]               0
           Conv2d-60          [-1, 512, 28, 28]         262,144
      BatchNorm2d-61          [-1, 512, 28, 28]           1,024
        LeakyReLU-62          [-1, 512, 28, 28]               0
         YoloConv-63          [-1, 512, 28, 28]               0
           Conv2d-64         [-1, 1024, 28, 28]       4,718,592
      BatchNorm2d-65         [-1, 1024, 28, 28]           2,048
        LeakyReLU-66         [-1, 1024, 28, 28]               0
         YoloConv-67         [-1, 1024, 28, 28]               0
        MaxPool2d-68         [-1, 1024, 14, 14]               0
           Conv2d-69          [-1, 512, 14, 14]         524,288
      BatchNorm2d-70          [-1, 512, 14, 14]           1,024
        LeakyReLU-71          [-1, 512, 14, 14]               0
         YoloConv-72          [-1, 512, 14, 14]               0
           Conv2d-73         [-1, 1024, 14, 14]       4,718,592
      BatchNorm2d-74         [-1, 1024, 14, 14]           2,048
        LeakyReLU-75         [-1, 1024, 14, 14]               0
         YoloConv-76         [-1, 1024, 14, 14]               0
           Conv2d-77          [-1, 512, 14, 14]         524,288
      BatchNorm2d-78          [-1, 512, 14, 14]           1,024
        LeakyReLU-79          [-1, 512, 14, 14]               0
         YoloConv-80          [-1, 512, 14, 14]               0
           Conv2d-81         [-1, 1024, 14, 14]       4,718,592
      BatchNorm2d-82         [-1, 1024, 14, 14]           2,048
        LeakyReLU-83         [-1, 1024, 14, 14]               0
         YoloConv-84         [-1, 1024, 14, 14]               0
           Conv2d-85         [-1, 1024, 14, 14]       9,437,184
      BatchNorm2d-86         [-1, 1024, 14, 14]           2,048
        LeakyReLU-87         [-1, 1024, 14, 14]               0
         YoloConv-88         [-1, 1024, 14, 14]               0
           Conv2d-89           [-1, 1024, 7, 7]       9,437,184
      BatchNorm2d-90           [-1, 1024, 7, 7]           2,048
        LeakyReLU-91           [-1, 1024, 7, 7]               0
         YoloConv-92           [-1, 1024, 7, 7]               0
           Conv2d-93           [-1, 1024, 7, 7]       9,437,184
      BatchNorm2d-94           [-1, 1024, 7, 7]           2,048
        LeakyReLU-95           [-1, 1024, 7, 7]               0
         YoloConv-96           [-1, 1024, 7, 7]               0
           Conv2d-97           [-1, 1024, 7, 7]       9,437,184
      BatchNorm2d-98           [-1, 1024, 7, 7]           2,048
        LeakyReLU-99           [-1, 1024, 7, 7]               0
        YoloConv-100           [-1, 1024, 7, 7]               0
         Flatten-101                [-1, 50176]               0
          Linear-102                 [-1, 4096]     205,524,992
       LeakyReLU-103                 [-1, 4096]               0
          Linear-104                 [-1, 1470]       6,022,590
================================================================
Total params: 271,716,734
Trainable params: 271,716,734
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 2.30
Forward/backward pass size (MB): 436.86
Params size (MB): 1036.52
Estimated Total Size (MB): 1475.68
----------------------------------------------------------------

파라미터는 2억7천만개로
살짝 로컬 PC에서 훈련/검증을 수행하기에는
난이도가 있는 Backbone모델이라 보면 된다.

profile
자율차 공부중

0개의 댓글