SSD: Single Shot MultiBox Detector 논문 요약
Abstract
본 연구는 single deep neural network로 object detection을 수행한다. SSD라는 접근 방식은 bounding box를 피처맵의 위치별로 종횡비와 배율이 다른 default box 집합으로 이산화하는 것이다. 네트워크는 default box내에 있는 각각의 카테고리에 대해 점수를 부여하고 객체의 형태에 맞도록 box를 조정한다. 또한, 다양한 resolution을 갖는 피처맵을 결합하여 object의 사이즈가 다양하더라도 예측을 잘 수행할 수 있게한다. SSD는 region proposals이나 features resampling 단계를 없애고 모든 계산을 단일 네트워크에서 캡슐화 하기때문에 object proposals이 필요한 다른 네트워크에 비해 간단하다. PASCAL VOC, COCO, ILSVRC datsets에서 실험한 결과, SSD는 region proposals이 필요한(RCNN 등) 다른 모델들에 비해 준수한 정확도를 가진다. VOC 2007 에서 300 x 300 이미지를 사용했을 때 74.3% mAP, 59FPS를 기록했고, 512 x 512 이미지에서는 76.9% mAP를 기록했다. 대회 SOTA인 Fast R-CNN의 성능보다 더 좋은 성능이다. 또한, 다른 single stage methods와 비교해도 적은 input size로 더 좋은 정확도를 가졌다.
Details
Introduction
- 최근 object detection 모델은 bounding box를 제안 받고, 각 bbox에 대해 pixel or features를 resampling하고, 좋은 분류기를 연결하는 등의 접근 방식을 조금 변형해서 속도와 성능을 높였다. Fast R-CNN은 대표적인 모델로써 PASCAL, COCO 등의 탐지 대회에서 좋은 성능을 가졌다.
- 이러한 접근 방식은 정확하긴하지만 계산 비용이 너무 크고 속도가 너무 느리다는 단점을 가지고 있다. 속도는 프레임당 초를 의미하는 SFP로 비교하는데 가장 빠르다는 Fast R-CNN조차 7 FPS를 가지고 있다. 현재는 속도를 얻는 대가로 정확도를 희생시키는게 전부인 추세다.
- 본 논문은 bounding box 가설을 위한 pixel or featrues resampling을 수행하지 않아 속도를 크게 향상 시킨 deep neural network 기반의 object detection을 소개한다. 속도 향상을 위해 위의 작업을 제거한 것이 처음은 아니지만 몇가지 개선사항을 추가해 이전보다 정확도를 크게 높일 수 있었다.
- 개선 사항으로는 작은 convolution filter를 사용하여 bounding box 위치의 offset과 object categories를 예측하는데 이 filter는 분리되어서 각각 다른 종횡비로 탐지를 수행하고 분리된 filter를 통해 여러개의 피처맵을 결합하여 다양한 scale의 object도 잘 탐지할 수 있도록했다.
- 특히, 각각 다른 scale 정보를 가지고 예측을 수행하도록 multiple layer를 만들면 상대적으로 낮은 resolution input을 가지고도 높은 정확도와 빠른 속도를 낼 수 있었다.
- SSD 연구가 기여할 수 있는 부분은 다음과 같다.
- Single shot detetcor의 이전 연구이자 SOTA였던 YOLO보다 빠르고, region proposals과 pooling을 사용한 다른 느린 모델(Fast R-CNN 포함)보다 더 정확한 single shot detector(SSD) for multiple categories를 소개한다.
- SSD의 핵심은 피처맵에 적용된 작은 convolutional filter를 사용하는 fixed default box 집합을 통해 category scores와 bbox의 offsets을 예측하는 것이다.
- 정확도를 높이기 위해 다양한 scale의 피처맵에서 다양한 scale의 예측을 수행하고 종횡비별로 분리된 예측을 수행했다.
- 이러한 구조로 단일 네트워크를 구축하고 low resolution input에서도 높은 정확도를 낼 수 있었고 속도와 정확도의 trade-off도 개선시켰다.
- 실험 파트에서는 속도와 정확도를 평가지표로 삼아 PASCAL, COCO, ILSVRC datasets에서 각각 다른 image size를 사용했을 때 결과가 어떻게 달라지는지, 다른 SOTA 모델들과 어떤 차이가 있는지 실험했다.
The Single SHot Detector(SSD)
위의 그림은 SSD의 프레임워크를 나타낸 것으로, SSD는 CNN 단계에서 각각의 다른 크기의 피처맵을 통해 위치마다 다른 종횡비를 가진 몇개의 default box들을 평가한다. 또한, default box의 예측값으로 default box의 shape offsets과 confidences를 출력한다. 예를 들어, 먼저 default box들과 정답 box를 비교할 때 Figure 1.을 보면 8x8 피처맵에서는 2개의 default box가 고양이를, 4x4 피처맵에서는 하나의 default box가 개를 예측했고, 8x8 피처맵에서는 상대적으로 크기가 큰 개에 대해서는 예측을 잘 수행하지 못했다. 이렇게 default box 중 물체가 존재한다고 판단한 box를 positive, 그 외의 박스는 negative라고 할 수 있다. 모델의 loss는 localization loss(confidence loss(ex. softmax) 포함)를 가중합하여 계산한다.
추가적으로, 피처맵의 크기가 작다는 것은 그만큼 큰 filter를 썼다는 것이기때문에 크기가 작은 피처맵에서는 큰 객체에 대한 탐지가 가능하다. SSD에서는 이처럼 피처맵의 크기를 layer마다 다르게해서 다양한 scale의 object를 잘 탐지하도록 한다.
1. Model
SSD는 CNN을 기반으로 고정된 크기의 bbox들을 처리하고, bbox 내 존재하는 object들의 존재확신도 점수를 매긴다. 마지막 detection에서는 NMS 기법을 통해 중복된 박스를 제거하여 객체당 하나의 박스만 존재하도록 한다. 네트워크 앞단에는 image classification에서 높은 성능을 자랑했던 high quailty 구조를 사용(base network)하고 그 후 layer들을 추가하여 detection tasks를 수행하도록 한다.
Multi-scale feature maps for detection
- Base network에 CNN 구조를 연결한다. 새로 연결한 layers 에서는 피처맵의 사이즈를 점점 줄여나가면서 multiple scales에서 예측을 수행하도록 한다.
- 각각의 feature layer마다 다른 scale의 예측을 수행하게 된다.
Convolutional predictors for detection
- 추가된 layer(아니면 base network에서 일부 layer를 더 활용해도됨)에서는 filter를 통해 각 층마다 고정된 detection predictions 집합(Figure 2.를 보면 conv 층 3x3 필터에 고정된 갯수의 default box가 생성된 것을 알 수 있음)을 생성할 수 있다.
- Layer에서는 mxnxp의 사이즈를 갖는 피처맵이 도출되는데, 기본적으로는 detection을 위해 3x3xp filter를 통해 cagteory scores와 default box coordinates를 계산한다.
- Bounding box의 offsets은 각 피처맵 위치에 존재하는 default box position과의 상대적 거리를 통해 계산된다(YOLO에서는 offsets을 구하기위해 CNN이 아닌 FC layer를 사용했음).
Defalut boxes and aspect ratios
- Default box들의 집합은 네트워크 상단에 있는 여러개의 피처맵 셀들에 연결된다. 이 box들은 피처맵 셀의 알맞은 위치에 고정된다. 그리고 각 피처맵 셀에서 default box의 모양을 기준으로 offsets을 예측하고, 각 box 내 object들의 물체 존재 확신도를 예측한다.
- 한 픽셀마다 k개 box들의 offsets 위치 4개와 c개의 클래스의 물체 존재 확신도 점수를 계산한다. 그 결과, mxn 피처맵에 대해 (c + 4)kmn의 결과가 도출된다.
- Default box는 Faster R-CNN의 anchor boxes와 비슷한 개념이지만 다양한 resolution을 가진 피처맵을 사용했다는 점에서 차이가 존재한다.
2. Training
SSD와 다른 detector와의 region proposals 단계에서 가장 큰 차이점은 SSD는 ground truth 정보가 고정된 detector outputs 집합에 꼭 할당 되어야한다는 것이다. 이 방법은 YOLO, Faster R-CNN, MultiBox 등 다른 모델들에서도 사용된다. 이 정보가 한번 할당되면 loss function과 역전파 전 과정에 적용된다. 학습에는 default boxes의 갯수와 scales을 얼마나 정할 것인지와 hard negative mining, data augmentation 등이 포함된다.
Matching strategy
- 학습을 할 때는 어떤 default box가 ground truth와 관련이 있는지 파악하고, 이에 따라 네트워크를 훈련해야한다. 이는 ground truth box와 가장 많이 겹치는 default box를 선택한다. Multibox 모델과는 달리, 어떤 ground truth냐와는 상관없이 jaccard overlap(IOU)이 threshold인 0.5보다 큰 default box라면 모두 정답과 연관이 있다고 판단한다.
- 이 방법은 가장 overlap이 큰 box를 하나만 남겨두는 것보다 오히려 다양한 scale을 가진 default box들을 통해 예측을 수행하여 learning problem을 단순화시킨다.
Training objective
- SSD의 손실 함수는 MultiBox 모델과 유사하지만 multiple object categories를 처리하도록 수정되었다.
- $x_{ij}^p = [1,0]$를 p라는 category에 대해 i번째 default box와 j번째 ground truth가 연결되었는지를 나타낸다고 하자.
- default box가 여러개 있기때문에 ground box와 일치된다고 판단된 경우가 많다면 $\Sigma_{i} x_{ij}^p \geq 1$ 이 될 수 있다.
- 전체 손실함수는 다음과 같이 정의된다.
- N은 matching된 default box의 갯수를 의미하고 N = 0이면, loss를 0으로 설정한다.
- Localization loss는 predicted box($l$)과 ground truth box($g$)간의 Smooth L1 loss를 적용한다.
- Faster-RCNN과 비슷하게 default box($d$)에 대해 center($cx, cy$)와 $w,h$를 offsets으로 사용한다.
- Confidence loss는 multiple class confidences($c$)에 대한 softmax를 활용한다.
- Cross validation을 통해 가중치항 $\alpha$는 1로 설정한다.
- 정리하자면 손실함수는 ground truth와 매칭되는 default box들의 손실로 계산되는데
- localization에 대한 손실을 offsets으로 계산하고
- box내 예측한 여러 class에 대한 손실을 softmax로 계산하여 두 손실을 합치는 방법을 사용한다.
- 합쳐진 두 손실은 matching된 box의 수로 나눠 최종 loss를 구한다.
Choosing scales and aspect ratios for default boxes
- Object의 다양한 scale을 처리하기위해 어떤 모델은 이미지 사이즈를 직접적으로 처리하는 방법을 사용하지만 SSD에서는 하나의 단일 네트워크에서 layer마다 다른 scale을 갖는 피처맵을 활용하고 이를 공유한다.
- 이전 연구들에 따라 층이 얕을수록 더 좁고 세밀한 정보를 파악할 수 있기때문에 얕은층과 깊은층의 피처맵들을 detection에 사용했다. 위의 Figure 1.에서 8x8과 4x4 피처맵이 그 예시이다.
- 층마다 피처맵의 크기가 다르다면 층마다 receptive field가 달라지게될텐데 다행히 SSD의 default box는 꼭 실제 receptive fields와 box의 크기를 일치시킬 필요는 없다. 피처맵이 특정 objects의 scale에 맞게 학습되도록 default box의 크기를 조정할 수 있기때문이다. 특정 예측을 위해 m개의 피처맵이 필요하고 각 층을 k라고 한다면 default box의 scale을 다음과 같이 계산할 수 있다.
- $S_{min}$이 0.2, $S_{max}$가 0.9라는 것은 가장 얕은 층의 scale이 0.2, 가장 깊은 층의 scale이 0.9라는 의미이다. 모든 층의 scale은 이 안에 있다.
- 다양한 default box의 aspect ratio는 $a_r \in [1,2,3,1/2,1/3]$ 으로 정의하고(1은 가로세로 비율이 같은 것, 2는 세로가 가로보다 2배 큰 것, 1/2는 가로가 세로보다 2배 큰 것) box마다 width는 $w_a^k = s_k \sqrt{a_r}$, height는 $h_a^k = s_k \sqrt{a_r}$로 계산한다.
- 이러한 방법으로 default box의 크기를 유연하게 조절하며 다양한 scale에서 bounding box를 그리고 추출된 수많은 피처맵들을 통합하기때문에 특정 피처맵에서 객체를 발견하지 못하더라도 다른 피처맵에서 객체를 발견하여 예측에 큰 도움을 준다.
Hard negative mining
- Matching 단계 이후에는 전경보다 배경이 훨씬 많기때문에 많은 default box들이 negative일 것이다.
- 모든 negative examples을 쓰는 대신 각 default box마다 confidence loss를 정렬하고 가장 좋은 box를 선정해서 negative와 positive의 비율이 최대 3:1 정도가 되도록 한다.
- 이를 통해 최적화가 더 빨라지고 안정적인 학습이 이루어질 수 있다.
Data augmentation
- 모델을 더 robust하게 만들기 위해서 input object의 크기나 형태를 변형하고 다음과 같은 option으로 input image를 랜덤하게 샘플링했다.
- Original input image
- IOU가 0.1, 0.3, 0.5, 0.7, 0.9인 객체들을 패치로 샘플링
- 랜덤하게 패치로 샘플링
- patch의 크기는 원본 이미지의 0.1과 1사이로 하고 종횡비는 0.5와 2사이로 한다. 샘플링된 패치에도 ground truth box가 겹쳐있다면 예측에 사용한다.
- 샘플링 단계 이후에는 샘플된 패치들을 고정된 크기로 리사이징하고 0.5의 확률로 수평변환시킨다. 추가로 photo-metric distortions을 적용한다.
Experimental Results
Base network
- 사전훈련 모델로 VGG16을 사용하고 DeepLab-LargeFOV처럼 fc6와 fc7 layer를 convolutional layers로 변환시켰다. 또한, Pool5의 2x2(s2)를 3x3(s1)으로 바꾸고 atrous algorithm을 사용했다. 모든 dropout layers와 fc8 층을 제거했다.
- Fine tuning에는 optimizer SGD, learning rate 0.001, momentum 0.9, weight decay 0.0005, batch size 32를 사용했다.
1. PASCAL VOC 2007
- Fast-RCNN, Faster R-CNN과 성능을 비교했다.
- SSD300은 이미지 사이즈를 300x300으로 조정한 모델이고 conv4_3, conv7(fc7), conv8_2, conv9_2, conv10_2, conv11_2에서 location과 confidences를 계산했다. Conv4_3의 default box scale은 0.1로 설정했고, 새로 추가된 convolutional layer의 파라미터는 xavier 방법으로 초기화했다.
- Conv4_3, conv10_2, conv11_2에는 각각의 피처맵 셀마다 4개의 default box를 사용했고, 다른 모든 layers에는 6개의 default box를 사용했다. 특히, conv4_3은 다른 layers들과 다른 feature scale을 가지고 있어서 L2 정규화로 feature norm을 20으로 조정하고 역전파 과정에서 scale을 학습했다.
- 위의 Table 1을 보면 SSD300 모델이 Fast R-CNN보다 높은 정확도를 가진다는 것을 알 수 있고, SSD512로 이미지를 키웠을 때는 Faster R-CNN보다 높은 정확도를 기록했다. COCO 데이터를 추가하는 등 더 많은 data로 학습을 진행했을 때는 SSD512 모델에서 81.6% mAP를 기록했다.
- Figure 3.을 보면 white area(correct)가 넓은 것을 보아 SSD model이 다양한 category에 대해 예측을 잘 수행한 것을 알 수 있다. Recall도 85~90% 정도였고 IOU threshold를 0.1로 낮추면 더 높은 Recall을 보인다.
- SSD는 R-CNN처럼 두 가지의 step을 걸쳐 localization과 classification이 다르게 이루어지지 않고 하나의 네트워크에서 직접 bbox와 class를 예측하기때문에 R-CNN보다 localization error가 작다.
- 하지만, 다양한 category들의 정보를 공유하기때문에 비슷한 objects에 대해서는 예측을 잘 수행하지 못한다.
- Figure 4.를 보면 SSD는 bounding box size에 굉장히 민감한 것을 알 수 있고 큰 box보다 작은 box 때문에 성능이 더 하락했다. 작은 객체는 깊은 layer에서 정보를 얻어내기가 힘들기때문에 어찌보면 당연한 결과다(top layers에서는 세밀한 정보보다 주로 전체적인 context나 패턴을 파악하기때문에).
- 300에서 512로 사이즈를 키워도 small object에 대한 탐지가 어려웠지만 그럼에도 다양한 scale의 박스와 피처맵을 통해 강건한 모델을 만들었다는 점에서 긍정적인 실험이었다.
2. Model analysis
SSD를 더 잘 이해하기위해 각각의 component들이 성능에 어떻게 영향을 미치는지 살펴보자. components의 영향을 잘 파악하기위해 모든 실험에 300 x 300사이즈의 이미지를 사용했다.
Data augmentation is crucial
- Fast, Faster R-CNN은 원본이미지와 수평변환 이미지를 학습에 사용했다. SSD는 YOLO와 비슷한 방법으로 sampling을 수행해 성능을 향상시켰다.
- SSD의 샘플링 기법이 Fast, Faster R-CNN에도 효율적일진 모르겠지만 object의 robust에 중요한 단계인 classification 중간에 pooling이 있기때문에 아마 효과가 좋진않을 것이다.
More default box shapes is better
- SSD는 피처맵 location마다 보통 6개의 default box를 사용했다. 만약 aspect ratios $\frac{1}{3}, 3$으로 boxes를 제거하면 성능이 하락했고, $\frac{1}{2}, 2$도 마찬가지였다.
- 다양한 크기의 default boxes를 사용하는 것이 성능 향상에 도움이 된다.
Atrous is faster
- DeepLab 모델처럼 VGG16에 atrous convolution을 적용했다.
- 만약, VGG16을 그대로 사용해서 pool5(2x2, s2) 사용, fc6, fc7의 파라미터에 subsampling 적용 X, conv5_3을 추가하는 방법을 사용하면 정확도는 같은데 속도는 20% 느려졌다.
Multiple output layers at different resolutions is better
- SSD는 다양한 output layers에서 다양한 scale의 default box를 사용하는 것이 핵심이기때문에 layers를 점점 줄여가면서 이 효과를 검증해봤다.
- 정확한 비교를 위해 layer를 제거할 때마다 default box tiling(각 픽셀마다 default box를 잘 배열하는 것)을 조정하여 총 상자 수를 원본과 비슷한 8732개로 유지했다(하지만, 모든 실험에서 tiling을 한 것은 아님). 남은 layer에 box scale을 더 많이 쌓고 필요한 경우 box scale을 조정했다. layer에 box를 쌓을 때 box가 이미지 경계에 있는 경우가 많으므로 주의하며 쌓아야한다.
- Table 3.는 layer가 적을수록 성능이 떨어지는 것을 보여준다.
- Faster R-CNN처럼 경계에 있는 box를 무시한채 예측을 진행했더니 흥미로운 사실을 발견했다. 만약 11_2, 10_2와 같은 깊은 layer를 사용한다면 성능이 크게 떨어진다는 것이다. 아마 이미지 경계에 있는 box들이 예측에 포함되지않아 큰 객체를 감쌀 수 있는 큰 box가 충분치 않아서일 것이다.
- 또한, conv7만을 사용했을 때 최악의 성능이 나왔는데 이것으로 다양한 scale의 box를 여러 layer에 적절히 분배하는 것이 성능에 큰 영향을 끼친다는 것을 알 수 있다.
- SSD는 lower resolution(300x300) input image를 사용했는데도 Faster R-CNN과 비슷한 정확도를 가졌다.
3. PASCAL VOC2012
- VOC2007과 똑같은 setting으로 실험을 진행한 결과 같은 performance를 보였다. SSD 300은 Fast/Faster R-CNN보다 정확도가 높았고 SSD512는 Faster R-CNN보다 4.5% 더 높은 mAP를 기록했다.
- YOLO와 비교했을 때도 훨씬 좋은 성능을 가졌고, COCO dataset으로 추가학습을 하면 mAP가 80%였다.
4. COCO & ILSVRC
- COCO는 PASCAL보다 객체가 좀 더 작기때문에 모든 레이어에 small default box를 사용했다. 따라서 최소 scale 0.2를 0.15로 조정하고 conv4_3의 scale을 0.07로 조정했다.
- SSD512는 conv12-2를 추가했고, $S_{min}$을 0.1, conv4_3을 0.04의 scale로 조정했다.
- PASCAL과 비슷하게 SSD300이 Fast R-CNN보다 성능이 좋았지만 mAP@0.75에서는 Faster R-CNN과 성능이 비슷했고 mAP@0.5에서는 성능이 좀 더 낮았다.
- SSD512에서는 mAP@0.75와 mAP@0.5 모두에서 Faster R-CNN보다 성능이 좋았다. 하지만, 작은 객체에 대해서는 Faster R-CNN과 큰 격차를 내진 못했는데 아마 Faster R-CNN은 RPN(Region Proposals Network)과 Fast R-CNN(분류 및 bbox 예측) 두 단계에 걸쳐 box에 대한 세밀한 조정이 이루어지기때문에 작은 객체에 대해 잘 예측할 수 있었을 것으로 추측된다.
- ILSVRC에서도 COCO와 동일한 네트워크를 사용했고 그 결과 val2 set에서 43.4% mAP를 달성했다.
5. Data Augmentation for Small Object Accuracy
- 데이터 증강은 PASCAL VOC와 같은 작은 datasets에서 성능을 효과적으로 높일 수 있다. 이미지를 랜덤하게 자르는 방법이 ‘zoom in’의 효과처럼 크기가 큰 데이터들을 만들어낼 수 있는 것이다.
- 크기가 작은 데이터를 만들어내기 위해 ‘zoom out’ 효과를 적용하려면 원본 이미지를 랜덤하게 자르기 전에 평균값으로 채워졌고 원본 이미지보다 16배 큰 캔버스에 이미지들을 랜덤하게 위치시킨다. 이렇게 zoom out 된 상태에서 crop을 진행하면 크기가 작은 data를 생성할 수 있는 것이다. 그 결과 여러 데이터셋에서 mAP가 2~3% 정도 증가했다.
- SSD를 개선하는 또 다른 방법은 default box의 tiling을 더 잘 디자인해서 receptive field와 default box가 잘 맞도록 하는 것이다. 이 task는 향후 연구를 위해 남겨둔다.
6. Inference time
- SSD에서는 수많은 box를 처리해야하기때문에 inference 과정에서 NMS를 잘 활용하는 것이 매우 중요하다. 먼저, confidence threshold를 0.01로 설정하면 많은 box들을 제거할 수 있고, 그 후 IOU threshold 0.45를 사용한 NMS를 통해 이미지당 200개의 box만 남도록 유지할 수 있다.
- NMS를 통해 SSD300은 VOC 200 classes에 대해 이미지당 1.7 msec 밖에 소요되지 않는다.
- Table.7을 보면 SSD300과 SSD512 모두 Faster R-CNN의 정확도와 속도를 능가한다. SSD300은 70% 이상의 mAP를 달성한 최초의 real-time method 이다.
- SSD 모델의 forward time 중 80%는 base network(VGG16)에서 소요된 것이기때문에 base network를 더 빠른 네트워크로 구축하면 속도면에서 향상되어 SSD512도 real-time으로 만들 수 있을 것이다.
Related Work
- 이미지에서 객체를 탐지하는 방법은 sliding window를 활용하는 방법과 region proposals를 활용하는 방법이 있다. R-CNN이 성능을 크게 향상시키자 region proposals을 기반으로 한 object detection이 트렌드가 되었다.
- R-CNN은 다양한 방법을 통해 모델을 개선시켜나갔는데 첫번째 방법으로는 classification에 소요되는 시간을 줄이는 접근이다. SPPPnet에서는 spatial pyramid pooling layer를 적용하여 region size와 scale을 robust하게 만들었고, 여러 이미지 resolution을 통해 생성된 피처맵들을 classification layer에서 다시 사용하므로 속도 문제를 개선시켰다. Fast R-CNN은 SPPnet을 보완시켜서 모든 layers를 fine-tuning 시켜서 confidences와 bounding box regression loss를 최소화시켰다.
- R-CNN을 개선시킨 두번째 방법으로는 deep neural network를 사용하여 proposal quaility를 개선시키는 접근이다. MultiBox와 같은 최근 모델에서는 low-level image features를 기반으로 한 selective search region proposals을 deep neural network에서 직접적으로 생성되는 proposals로 대체했다. 이 방법은 정확도를 증가시켰지만 proprosals과 classification을 위해 종속된 두 가지 neural network를 학습시켜야했으므로 복잡하다는 단점이 있다.
- Faster R-CNN에서는 selective search를 RPN으로 대체하고 기존 Fast R-CNN에 RPN을 통합하여 CNN과 prediction layers가 정보를 잘 공유할 수 있도록 fine-tuning을 진행했다.
- SSD는 RPN과 비슷한 방법을 사용하는데 Faster R-CNN에서 anchor boxes를 사용한 것처럼 SSD는 고정된 default box 집합을 사용한다는 것이다. 하지만, Faster R-CNN에서는 pool feature를 추출하고 평가는 다른 분류기에서 이루어졌는데 SSD는 각각의 box마다 여러 object category에 대한 score를 계산할 수 있어 특징 추출과 평가가 동시에 이루어진다는 점에서 차이가 있다. 따라서, SSD는 여러 tasks들을 통합하여 Faster R-CNN보다 학습이 쉽고 빠른 모델을 만들 수 있었다.
- SSD와 비슷한 또 다른 접근 방식은 proposals 단계를 생략하고 여러 categories에 대해 bbox와 confidence를 바로 예측하는 것이다. Sliding window 기법의 심층 버전인 OverFeat은 object categories의 confidence를 얻은 후에 각 위치마다 가장 좋은 피처맵을 사용해서 bounding box를 예측한다. YOLO는 가장 좋은 피처맵을 통해 multiple categories의 confidences와 bounding box를 예측한다.
- SSD는 이 두가지 모델처럼 proposals step이 없고 default box를 사용하여 confidence와 bbox를 예측하기때문에 비슷한 접근방식이라고 할 수 있다. 하지만, 다양한 scales의 피처맵 location에서 다양한 종횡비를 가진 bbox를 사용하기때문에 기존 접근 방식보다 더 유연한 방법이라고 할 수 있다.
- 만약 default box를 가장 좋은 하나의 피처맵 location에서만 사용한다면 SSD는 OverFeat과 비슷한 구조를 가졌을 것이고, 가장 좋은 피처맵을 사용하면서 convoutional layer대신 fully connected layer를 추가하고 다양한 aspect ratios를 고려하지 않는다면 YOLO와 비슷한 모델이 될 것이다.
Conclusion
- SSD는 multiple feature maps에 적용된 다양한 scale의 convolutional bounding box를 사용하는 fast single-shot object detector이다.
- 많은 default box들이 성능을 향상시켰고 기존보다 몇배 더 많은 box predictions sampling location, scale, aspect ratio가 사용됐다.
- SSD512 모델은 기존 SOTA였던 Faster R-CNN보다 3배나 더 빠르면서 성능은 뛰어난 모델이고 SSD300은 58FPS로 YOLO보다 빠르고 정확한 real time 모델이다.
- SSD는 비디오에서 객체를 탐지하고 추적하는 모델의 일부로 사용되어 향후 연구에 좋은 영향을 끼칠 것이다.
개인적인 생각
- SSD는 단일 네트워크로 빠른 이미지 처리 뿐만 아니라 다양한 scale의 default box를 사용함으로써 다양한 객체의 크기에 유연하게 반응할 수 있다는 점에서 매우 의미있는 연구였다.
- YOLO와 비슷하게 SOTA 모델인 R-CNN 계열의 모델들이나 비슷하거나 다른 접근방식을 가진 다양한 모델들과 성능을 비교했고, 좋았던 부분이나 부족한 부분을 시사할 수 있었기때문에 이를 바탕으로 부족한 부분을 개선하면 향후 연구에도 큰 도움이 될 것이다.
- VGG보다 더 좋은 성능을 내는 GoogLeNet, ResNet과 같은 모델을 기반으로 pre-trained 시켰다면 본 논문에서도 언급한바와 같이 더 빠르고 정확한 모델이 만들어지진 않았을까라는 생각이 들었다.
- Multiple-scale의 default box를 사용하더라도 결국 작은 객체에 대해서는 탐지가 쉽지않았는데 향후 연구에서는 작은 객체를 잘 탐지하도록 어떤 기술을 사용할지 궁금하다.
- SSD와 YOLO 모두 one stage detector로 region proposals과 분류 및 box prediction이 단일 네트워크에서 가능하게해서 R-CNN과 같은 two stage detector보다 속도를 더 빠르게했다. 2개의 분리된 단계를 하나의 단계로 압축시켰기때문에 어찌보면 속도를 가장 잘 줄일 수 있는 방법을 사용했다고 볼 수 있는데 앞으로는 어떠한 기술들로 정확도를 크게 희생시키지않으면서 속도를 더 빠르게 할지, 더 나아가 정확도도 크게 개선시키고 속도도 크게 줄일지 기대가 된다.
구현
SSDLite 모델을 사용해보자(코드 참고). 데이터셋 및 파일 경로 설정은 Fast & Faster RCNN 포스팅 구현 파트 참고.
import json
import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
from tqdm import tqdm
import torch
from pycocotools.coco import COCO
from PIL import Image
import time
import transforms as T
from engine import train_one_epoch, evaluate
import utils
from drive.MyDrive.paper_practice.custom_dataset.soccer_dataset import SoccerDataset # SoccerDataset 모듈화
import albumentations as A
import transforms as T
def get_transforms(train):
transforms = []
# if train:
# transforms.append(A.HorizontalFlip(0.5))
# transforms.append(A.VerticalFlip(0.5))
return A.Compose(transforms, bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))
# label_fields는 호출할 때 입력한 key와 맞아야함. key이름을 labels로 했으니까 field로 labels
# 이미 x2,y2형식으로 바꿨으니까 coco가 아닌 pascal 형식
import torchvision
from torchvision.models.detection.ssdlite import SSDLiteClassificationHead
from torchvision.models.detection import _utils as det_utils
from functools import partial
from torch import nn
model = torchvision.models.detection.ssdlite320_mobilenet_v3_large(pretrained = True)
num_classes = 3 # has ball, no ball, background
# backbone 끝단의 output을 head의 input으로 사용하고 이미지 크기는 640x640으로
in_channels = det_utils.retrieve_out_channels(model.backbone, (640, 640))
num_anchors = model.anchor_generator.num_anchors_per_location() # 기존 앵커 그대로 사용
norm_layer = partial(nn.BatchNorm2d, eps=0.001, momentum=0.03) # 정규화 layer 설정
# regression은 위치만 보기 때문에 class 정보가 필요하지 않음. 앵커도 기존의 앵커 비율을 사용했기때문에 굳이 수정 X
model.head.classification_head = SSDLiteClassificationHead(in_channels, num_anchors, num_classes, norm_layer)
device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model.to(device)
# forward 테스트
a = SoccerDataset(TR_DATA_PATH, TR_LAB_PATH, get_transforms(train=True))
dl = torch.utils.data.DataLoader(a, batch_size=8, shuffle=True
collate_fn=utils.collate_fn)
images,targets = next(iter(dl))
images = list(image.to(device) for image in images)
targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
output = model(images,targets)
output
# 데이터 정의
train_dataset = SoccerDataset(TR_DATA_PATH, TR_LAB_PATH, get_transforms(train=True))
val_dataset = SoccerDataset(VAL_DATA_PATH, VAL_LAB_PATH, get_transforms(train=False))
train_data_loader = torch.utils.data.DataLoader(train_dataset, batch_size=8, shuffle=True,
collate_fn = utils.collate_fn)
val_data_loader = torch.utils.data.DataLoader(val_dataset, batch_size=4, shuffle=False, # 재연성을 위해 셔플 False
collate_fn = utils.collate_fn)
num_epochs = 50
val_loss_tmp = 10000
best_epoch_tmp = 1
early_stopping_cnt = 0
early_stop = 20
params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.SGD(params, lr=0.001,
momentum=0.9, weight_decay=0.0005)
# lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
# step_size=3,
# gamma=0.9)
#lr_scheduler = torch.optim.lr_scheduler.MultiplicativeLR(optimizer=optimizer, lr_lambda=lambda lr: 0.95 ** lr)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=5, eta_min=0.0001)
print('----------------------train start--------------------------')
for epoch in range(1, num_epochs+1):
start = time.time()
model.train()
epoch_loss = 0
prog_bar = tqdm(train_data_loader, total=len(train_data_loader))
for images, targets in prog_bar:
images = list(image.to(device) for image in images)
targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
loss_dict = model(images, targets)
optimizer.zero_grad() # optimization 정보가 누적되지않도록 초기화
loss = sum(loss for loss in loss_dict.values())
loss.backward()
optimizer.step()
epoch_loss += loss.item()
print(f'epoch : {epoch}, Loss : {epoch_loss}, time : {time.time() - start}')
with torch.no_grad():
epoch_val_loss = 0
val_start = time.time()
for images, targets in val_data_loader:
images = list(image.to(device) for image in images)
targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
val_loss_dict = model(images, targets)
epoch_val_loss += sum(loss for loss in val_loss_dict.values())
print(f'Val Loss : {epoch_val_loss}, time : {time.time() - val_start}')
if epoch_val_loss < val_loss_tmp: # best 모델만 저장
early_stopping_cnt = 0
best_epoch_tmp = epoch
val_loss_tmp = epoch_val_loss
torch.save(model.state_dict(),f'{WEIGHTS_PATH}ssd_{num_epochs}.pt')
else:
early_stopping_cnt += 1 # 손실이 늘었으면 early_stopping count
print(f'현재까지 best 모델은 Epochs {best_epoch_tmp}번째 모델입니다.')
if early_stopping_cnt == early_stop:
print(f'{early_stop}번 동안 validation 성능 개선이 없어 학습을 조기 종료합니다.')
break
# test 데이터 정의 및 평가
test_dataset = SoccerDataset(TEST_DATA_PATH, TEST_LAB_PATH, get_transforms(train=False))
test_data_loader = torch.utils.data.DataLoader(test_dataset, batch_size=4, shuffle=False,
collate_fn = utils.collate_fn)
evaluate(model, test_data_loader, device=device)
# 결과 시각화
i, t = test_dataset[100]
model.to(device)
model.eval()
with torch.no_grad():
prediction = model([i.to(device)])[0]
i = np.array(i.permute((1, 2, 0)) * 255).astype(np.uint8).copy()
for idx, x in enumerate(prediction['boxes']):
x = np.array(x.cpu(), dtype = int)
cv2.rectangle(i, (x[0], x[1]), (x[2],x[3]), color = (0,255,0), thickness = 2)
cv2.putText(i, str(prediction['labels'][idx].tolist()), (x[0],x[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color = (255,0,0), thickness= 3)
plt.imshow(i)
nms 적용전 SSD 결과
from torchvision.ops import nms
selected_idx = nms(prediction['boxes'], prediction['scores'], iou_threshold = 0.2)
selected_boxes = torch.tensor(prediction['boxes'])[selected_idx]
selected_labels = torch.tensor(prediction['labels'])[selected_idx]
selected_scores = torch.tensor(prediction['scores'])[selected_idx]
i, t = test_dataset[100]
i = np.array(i.permute((1, 2, 0)) * 255).astype(np.uint8).copy()
for idx,x in enumerate(selected_boxes):
if selected_scores[idx] > 0.9:
x = np.array(x.cpu(), dtype = int)
cv2.rectangle(i, (x[0], x[1]), (x[2],x[3]), color = (0,255,0), thickness = 2)
cv2.putText(i, str(selected_labels[idx].tolist()), (x[0],x[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color = (255,0,0), thickness= 3)
plt.imshow(i)
nms 적용후 SSD 결과(confidence score가 0.9 이상일 때)
Faster R-CNN 결과
SSD의 가벼운 버전인 SSDLite를 사용했을 때 test set에서 mAP@0.5:0.95이 0.546이 나왔다. 이는 Faster R-CNN의 0.407보다 훨씬 높은 수치이다. 위의 이미지를 통해 SSD가 Faster R-CNN보다 localization과 classification을 모두 잘 수행한 것을 알 수 있다. SSD에서는 볼을 소유하고 있는 사람에게 1의 클래스를 잘 부여했지만, Faster R-CNN은 모든 사람을 볼을 소유하고 있지 않은 2의 클래스로 예측했다. 학습시간 또한 SSD는 한 에포크당 약 20초의 학습 시간이 걸렸는데 Faster R-CNN이 에포크당 3분 30초가 걸린 것과 비교하면 약 10배 이상 빠른 속도다.
논문에서 언급한대로 당시 SOTA model이었던 Faster R-CNN보다 정확도와 속도 모든 면에서 우월한 SSD였다.