YOLO튜토리얼 1
2020/04/26 - [Programmer/Deep Learning] - YOLO 튜토리얼 1.YOLO 란?
사전지식
1. residual blocks : Residual이라는 것을 일종의 오차로 residual learning은 입력으로부터 얼만큼v달라져야하는지를 학습한다. ResNet에서 shortcut Connection을 x라 했을 때 shortcut connection(몇개의 layer를 건너뛰는 것)과 합쳐지는 것은 bn(conv(relu(bn(conv(x)))))bn(conv(relu(bn(conv(x)))))가 된다. 이 연산과정을 다른 그림으로 보자면 다음과 같다. shortcut connection과 residual mapping을 더한 다음에 ReLU를 취하는 것을 볼 수 있다. 이렇게 하는 이유는 ReLU를 통과하면 +의 값만 남기 때문에 Residual의 의미가 제대로 유지되지 않기 때문이다. 이 연산을 하는 부분을 Residual Block이라고 부른다.
출처: https://datascienceschool.net/view-notebook/958022040c544257aa7ba88643d6c032/
2. skip connection : 네트워크의 입력과 출력이 더해진 것을 다음 레이어의 입력으로 사용하는 것으로 스킵 연결을 구현 하는 것은 덧셈 연산의 추가 만으로 가능하다. 이는 추가적인 연산량이나 파라미터가 많이 필요하지 않다. 또한 역전파 시에 그레디언트가 잘 흘러갈 수 있게 해준다는 장점도 있다.
3. upsampling : upsampling을 통해 네트워크는 작은 물체를 감지하는 데 도움이 된다. 미세한 기능을 학습할 수 있다.
4. Bounding box regression
5. object detection : 이미지 인식(Image Recognition) 종류중에 하나로 보통 classification 과 Localization을 동시에 수행하는 것을 말하지만 이미지 위에 모델이 학습한 Object 위치만 bounding box로 표현되고 class종류는 구분하지 않음.
6. IoU : 정답 box와 예측 box의 교집합 크기/합집합 크기 이다.
7. NMS(non-maximum suppression) : NMS는 동일한 이미지의 여러 detection 문제를 해결하려고 한다. 예를 들어, 레드 그리드 셀의 3개의 bounding box 모두가 박스를 검출하거나 인접 셀이 동일한 객체를 검출 할 수 있습니다. NMS는 이러한 multiple detection을 없애줍니다.
9. confidence score : 얼마나 박스 안에 실제로 object가 존재하는지, 그리고 그 class를 얼마나 잘 반영했는지에 대한 것
10. A Fully Convolutional Neural Network
FCN에서는 FC layer(를 지나는 순간 각 pixel에 대한 위치정보는 소실된다.)대신 1x1 convolution layer를 사용했다는 점이다. 이유에 대해 추측해보자면 Fully connected layer를 사용하기 위해서는 고정된 input size를 가질 수 밖에 없다. 왜냐하면 pixel에 대한 위치정보는 소실되기 때문이다. FCN의 Architecture는 크게 3단계정도로 나뉜다.
출처: https://reniew.github.io/18/
YOLO 튜토리얼 링크:
https://blog.paperspace.com/how-to-implement-a-yolo-object-detector-in-pytorch/
튜토리얼 코드
일단 YOLO는 기본적으로 YOLO저자가 만든 Darknet이라는 신경망 프레임워크(neural network framework)로, dnn(deep neural network)들을 학습시키고 실행시킬 수 있는 틀(framework)를 사용한다. (config file에다 모델의 스펙을 기술해주면 C에서 그 모델 모양 그대로 네트워크를 생성해준다)
여기에선 YOLO의 config file을 읽어서 C기반 cuda로 export해주는 기존 방식이 아닌 pytorch로 모델을 생성해주는 방식을 사용한다.
코딩 시작:
일단 프로젝트 폴더를 만들어서 YOLO의 전반적인 구조를 코딩할 darknet.py 파일과 여러 도움을 줄 함수들을 작성할 util.py파일을 그 안에 생성한다.
Part2: Creating the layers of the network architecture
1.Configuration file
cfg파일은 네트워크의 layout을 block단위로 정의해 놓은 파일이다.
[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
cfg파일을 열면 이런 소스가 보일 것이다.
각 괄호들은 하나의 네트워크적인 의미를 가지고 있고, 위의 경우 3개의 convolutional layer과 1개의 shortcut을 나타내고 있다.
Shortcut은 ResNet등에서 나온 skip connection과 유사하다.
YOLO에서는 6타입의 layer들이 있다.
shortcut에서 from=-3이라는 뜻은 shortcut layer의 output에 3번째 전의 layer을 더해준다는 뜻이다. 기존의 skip connection과 일치한다.
Upsample
[upsample]
stride=2
Feature map을 stride배수에 따라 upsample하는 layer이다.
Route
[route]
layers = -4
[route]
layers = -1, 61
Route layer의 layers라는 속성은 한 개 혹은 두개의 value를 가지고 있다.
layers속성에 한 개의 value만 있을 때는 그 value의 index에 해당하는 feature map의 value만을 output으로 내보낸다.
위의 예시 -4의 경우, 4번째 이전의 layer의 output layer을 내보낸다고 할 수 있다.
만약 두개의 value가 있을 때는, 그 value들의 layer들의 feature map을 이어붙여서(concatenate)내보낸다. 위의 예시 같은 경우에는 depth dimension의 직전 layer(-1)과 61번째 layer을 내보내는 코드이다.
YOLO
[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
YOLO는 layer은 이미지의 feature들을 뽑고 난 후에 실질적인 prediction을 하는 layer이다.
Anchor
bounding box의 width와 height를 예측하여 정답을 맞추는 것이 자연스러워 보이지만, 실제로 돌려보면 unstable gradient를 발생시키는 원인이 된다. 그래서 대신 anchor라고 하는 pre-defined default bounding box를 사용한다. ( 그렇지 않은 경우 log-space transforms 라는걸 사용하기도 한다. )
Mask
위에는 총 9개의 anchor이 정의되어 있는데, 그 중 mask에 적혀있는 tag에 해당하는 anchor들만 사용한다.
(10,13) , (16,30) , (33,23) 의 anchor만 사용한다는 뜻 !
그렇다면 결국 각 cell은 3개의 box를 예측하게 될 것이다.
Net
[net]
# Testing
batch=1
subdivisions=1
# Training
# batch=64
# subdivisions=16
width= 320
height = 320
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
마지막으로 net이라고 불리는 block인데, 이것은 layer이라고 부르기 보다는 네트워크 전반적인 설정을 구성해주는 block이다.
2.Parsing the configuration file(해석하고 실제 모델을 구현하는 코드)
2-1. darknet.py file에 기본적인 import를 한다.
from __future__ import division
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
2-2. parse_cfg 파일작성(configuration file을 input으로 받아서 cfg파일을 해석하는 함수를 작성, cfg파일의 모든 block을 dict형태로 저장하는 것을 목표)
def parse_cfg(cfgfile):
""" Takes a configuration file Returns a list of blocks. Each blocks describes a block in the neural network to be built. Block is represented as a dictionary in the list """
2-3. 파일 읽고 전처리 ,블록 담기
# 파일 읽고 전처리하기
file = open(cfgfile, 'r')
lines = file.read().split('\n') # store the lines in a list
lines = [x for x in lines if len(x) > 0] # get read of the empty lines
lines = [x for x in lines if x[0] != '#'] # get rid of comments
lines = [x.rstrip().lstrip() for x in lines] # get rid of fringe whitespaces
# 이제 블록을 담자
block = {}
blocks = []
for line in lines:
if line[0] == "[": # This marks the start of a new block
if len(block) != 0: # If block is not empty, implies it is storing values of previous block.
blocks.append(block) # add it the blocks list
block = {} # re-init the block
block["type"] = line[1:-1].rstrip()
else:
key,value = line.split("=")
block[key.rstrip()] = value.lstrip() # 띄어쓰기 기준으로 왼쪽 오른쪽 공백 제거
blocks.append(block)
return blocks
3. Building Block만들기 : parse_cfg함수에서 return된 list로부터 실제의 pytorch module을 만든다. 위에서 언급된 6개의 layer들 종류를 module로 옮긴다.
def create_modules(blocks):
net_info = blocks[0] #Captures the information about the input and pre-processing
module_list = nn.ModuleList()
prev_filters = 3
output_filters = []
for index, x in enumerate(blocks[1:]):
module = nn.Sequential()
#check the type of block
#create a new module for the block
#append to module_list
if (x["type"] == "convolutional"):
#Get the info about the layer
activation = x["activation"]
try:
batch_normalize = int(x["batch_normalize"])
bias = False
except:
batch_normalize = 0
bias = True
filters= int(x["filters"])
padding = int(x["pad"])
kernel_size = int(x["size"])
stride = int(x["stride"])
if padding:
pad = (kernel_size - 1) // 2
else:
pad = 0
#Add the convolutional layer
conv = nn.Conv2d(prev_filters, filters, kernel_size, stride, pad, bias = bias)
module.add_module("conv_{0}".format(index), conv)
#Add the Batch Norm Layer
if batch_normalize:
bn = nn.BatchNorm2d(filters)
module.add_module("batch_norm_{0}".format(index), bn)
#Check the activation.
#It is either Linear or a Leaky ReLU for YOLO
if activation == "leaky":
activn = nn.LeakyReLU(0.1, inplace = True)
module.add_module("leaky_{0}".format(index), activn)
#If it's an upsampling layer
#We use Bilinear2dUpsampling
elif (x["type"] == "upsample"):
stride = int(x["stride"])
upsample = nn.Upsample(scale_factor = 2, mode = "bilinear")
module.add_module("upsample_{}".format(index), upsample)
Route Layer/Shortcut Layer
#If it is a route layer
elif (x["type"] == "route"):
x["layers"] = x["layers"].split(',')
#Start of a route
start = int(x["layers"][0])
#end, if there exists one.
try:
end = int(x["layers"][1])
except:
end = 0
#Positive anotation
if start > 0:
start = start - index
if end > 0:
end = end - index
route = EmptyLayer()
module.add_module("route_{0}".format(index), route)
if end < 0:
filters = output_filters[index + start] + output_filters[index + end]
else:
filters= output_filters[index + start]
#shortcut corresponds to skip connection
elif x["type"] == "shortcut":
shortcut = EmptyLayer()
module.add_module("shortcut_{}".format(index), shortcut)
Empty Layer
class EmptyLayer(nn.Module):
def __init__(self):
super(EmptyLayer, self).__init__()
YOLO Layer
#Yolo is the detection layer
elif x["type"] == "yolo":
mask = x["mask"].split(",")
mask = [int(x) for x in mask]
anchors = x["anchors"].split(",")
anchors = [int(a) for a in anchors]
anchors = [(anchors[i], anchors[i+1]) for i in range(0, len(anchors),2)]
anchors = [anchors[i] for i in mask]
detection = DetectionLayer(anchors)
module.add_module("Detection_{}".format(index), detection)
Detection Layer
class DetectionLayer(nn.Module):
def __init__(self, anchors):
super(DetectionLayer, self).__init__()
self.anchors = anchors
함수 create_modules의 마지막 부분
module_list.append(module) prev_filters = filters output_filters.append(filters)
#다음 레이어로 넘어가기 위해 정보를 저장
return (net_info, module_list) #create_modules의 마지막엔 net_info와 module_list를 포함한 튜플을 리턴
정리
파일: yolov3.cfg(configuration file) , darknet.py( parse_cfg 함수 , create_modules 함수)
parse_cfg함수로 configuration 파일을 해석 -> create_modules 함수로 pytorch module을 만듦
'Programmer > Deep Learning' 카테고리의 다른 글
[논문리뷰] Histograms of Oriented Gradients for Human Detection (0) | 2020.04.28 |
---|---|
YOLO 튜토리얼 3. Implementing the forward pass of the network (0) | 2020.04.26 |
YOLO 튜토리얼 1.YOLO 란? (0) | 2020.04.26 |
[논문리뷰] Rapid Object Detection using a Boosted Cascade of Simple Features (0) | 2020.04.21 |
[논문리뷰] An Overview Of Face Liveness Detection 2 (0) | 2020.04.19 |