[Kaggle] Google AI Open Images - Object Detection Track
Kaggle Study #2. - Google AI Open Images - Object Detection Track
Object Detection에 대한 논문을 읽어가면서 이를 실제 데이터로 적용해보고 싶다는 생각에 과거 kaggle competition을 찾아보았습니다. 그래서 발견한 것이 2018년 진행된 Google AI Open Images - Object Detection Track 이었습니다. 3년전 대회이긴 하지만 discussion들이나 코드들을 참고하면서 공부할 목적으로 선택해보았습니다.
대회 Overview
먼저 Google AI Open Images - Object Detection Track에 대해 간단히 정리해보겠습니다. 본 대회는 99,999장의 test 이미지 파일에 대해서 object detection을 수행하는 것이 목적입니다.
Detecting을 제대로 해냈는지 평가하는 지표로 3가지를 살펴봅니다. Detecting한 object의 category, Box의 좌표, Confidence입니다. 즉, 이미지 내의 사물을 인식하고, 그 위치를 정확히 포착하고, 이에 대한 예측 확률이 높아야 좋은 rating을 받는 것이죠!
이미지 데이터에 대한 자세한 설명은 Open Images Challenge page에서 확인할 수 있습니다!
How to?
이 문제에 적용해보고 싶은 모델은 YOLO v3입니다. 최근 YOLO 논문들을 version별로 살펴보고 있는데 실제로 얼마나 빠르고 정확한지 확인해보고 싶었거든요!
여러 구현 방법들이 있지만 제가 선택한 방법은 darknet을 이용하는 것입니다. 실제 대회 참가가 아니고 우수 코드들을 보면서 공부하는 것이 목적이기 때문에, 우선 제 결과물은 빠르게 내는 것이 중요하다고 생각했습니다. darknet은 open source neural network framework로 C와 CUDA를 기반으로 짜여져 있어 매우 빠르면서도 코드를 파이썬에서도 쉽게 돌릴 수 있기 때문에 선택하게 되었습니다.
코드는 AlexeyAB의 darknet코드 깃헙을 clone하여 사용했습니다. 그리고 darknet을 make하기 전에 제 환경에 맞게 두 가지 customizing을 해주었습니다.
- Makefile 수정
깃헙 코드를 clone하면 그 안에 Makefile이라는 이름의 파일이 생성됩니다. 여러 옵션들을 지정할 수 있는 파일인데요. 저는 GPU, CUDNN, CUDNN_HALF, OPENCV 항목을 1로 바꾸어 주었습니다. (기존은 0) 이렇게 해야 GPU를 사용하여 detecting 할 수 있거든요!
- detector.c 수정
src 폴더 내의 detector.c라고 하는 c언어 코드를 일부 수정했습니다. AlexeyAB의 darknet 실행 코드는 몇 가지 옵션을 지정할 수 있습니다. 그 중에서 save_labels를 추가하면 detecting한 box의 정보들을 저장해줍니다. 이 때, detecting한 class의 id와 box의 가로 세로 정보가 default로 저장됩니다. 대회의 제출물에 필요한 정보는 class_id, xmin, ymin, xmax, ymax, confidence 값이기 때문에 이 정보들이 저장되도록 코드를 수정했습니다.
참고: detector.c를 수정하는 과정에서 conflicting types for 오류가 나면서 detecor.o를 build하는 데 실패하는 문제가 나타났습니다. 리눅스에서 종종 발생하는 문제라는데.. 이리저리 찾아보면서 헤맸는데 갑작스럽게 저절로 해결되었네요;; 지속적으로 이런 문제가 발생한다면 뭔가 조치가 필요할 것 같습니다.
// pseudo labeling concept - fast.ai
if (save_labels){
char labelpath[4096];
replace_image_to_label(input, labelpath);
FILE* fw = fopen(labelpath, "wb");
int i;
for (i = 0; i < nboxes; ++i) {
char buff[1024];
int class_id = -1;
float prob = 0;
for (j = 0; j < l.classes; ++j) {
if (dets[i].prob[j] > thresh && dets[i].prob[j] > prob) {
prob = dets[i].prob[j];
class_id = j;
}
}
if (class_id >= 0) {
sprintf(buff, "%d %2.4f %2.4f %2.4f %2.4f %2.4f\n", class_id,
prob, #add for confidence
dets[i].bbox.x - dets[i].bbox.w / 2., #add for xmin
dets[i].bbox.y - dets[i].bbox.h / 2., #add for ymin
dets[i].bbox.x + dets[i].bbox.w / 2., #add for xmax
dets[i].bbox.y + dets[i].bbox.h / 2.); #add for ymax
fwrite(buff, sizeof(char), strlen(buff), fw);
}
}
fclose(fw);
이제 다 됐습니다. 아래 실행 코드를 통해 test 이미지들에 대한 detecting을 수행하기만 하면 됩니다!
!./darknet detector test ./cfg/openimages.data ./cfg/yolov3-openimages.cfg yolov3-openimages.weights -dont_show < ./train.txt > result.txt -save_labels
잘 진행되는지 궁금하니까 한 장에 대해서만 수행 결과를 확인해보도록 하겠습니다. 아래 사진은 99,999장의 test 이미지 중 한 장을 가져온 것입니다.
위 사진을 가지고 실행코드를 돌려보면 아래와 같은 실행 결과를 얻을 수 있습니다.
/content/darknet
CUDA-version: 11000 (11020), cuDNN: 7.6.5, CUDNN_HALF=1, GPU count: 1
CUDNN_HALF=1
OpenCV version: 3.2.0
0 : compute_capability = 600, cudnn_half = 0, GPU: Tesla P100-PCIE-16GB
net.optimized_memory = 0
mini_batch = 1, batch = 1, time_steps = 1, train = 0
layer filters size/strd(dil) input output
0 Create CUDA-stream - 0
Create cudnn-handle 0
conv 32 3 x 3/ 1 608 x 608 x 3 -> 608 x 608 x 32 0.639 BF
1 conv 64 3 x 3/ 2 608 x 608 x 32 -> 304 x 304 x 64 3.407 BF
2 conv 32 1 x 1/ 1 304 x 304 x 64 -> 304 x 304 x 32 0.379 BF
3 conv 64 3 x 3/ 1 304 x 304 x 32 -> 304 x 304 x 64 3.407 BF
4 Shortcut Layer: 1, wt = 0, wn = 0, outputs: 304 x 304 x 64 0.006 BF
5 conv 128 3 x 3/ 2 304 x 304 x 64 -> 152 x 152 x 128 3.407 BF
6 conv 64 1 x 1/ 1 152 x 152 x 128 -> 152 x 152 x 64 0.379 BF
7 conv 128 3 x 3/ 1 152 x 152 x 64 -> 152 x 152 x 128 3.407 BF
8 Shortcut Layer: 5, wt = 0, wn = 0, outputs: 152 x 152 x 128 0.003 BF
9 conv 64 1 x 1/ 1 152 x 152 x 128 -> 152 x 152 x 64 0.379 BF
10 conv 128 3 x 3/ 1 152 x 152 x 64 -> 152 x 152 x 128 3.407 BF
11 Shortcut Layer: 8, wt = 0, wn = 0, outputs: 152 x 152 x 128 0.003 BF
12 conv 256 3 x 3/ 2 152 x 152 x 128 -> 76 x 76 x 256 3.407 BF
13 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
14 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
15 Shortcut Layer: 12, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
16 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
17 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
18 Shortcut Layer: 15, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
19 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
20 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
21 Shortcut Layer: 18, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
22 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
23 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
24 Shortcut Layer: 21, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
25 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
26 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
27 Shortcut Layer: 24, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
28 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
29 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
30 Shortcut Layer: 27, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
31 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
32 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
33 Shortcut Layer: 30, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
34 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
35 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
36 Shortcut Layer: 33, wt = 0, wn = 0, outputs: 76 x 76 x 256 0.001 BF
37 conv 512 3 x 3/ 2 76 x 76 x 256 -> 38 x 38 x 512 3.407 BF
38 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
39 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
40 Shortcut Layer: 37, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
41 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
42 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
43 Shortcut Layer: 40, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
44 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
45 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
46 Shortcut Layer: 43, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
47 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
48 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
49 Shortcut Layer: 46, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
50 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
51 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
52 Shortcut Layer: 49, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
53 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
54 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
55 Shortcut Layer: 52, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
56 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
57 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
58 Shortcut Layer: 55, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
59 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
60 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
61 Shortcut Layer: 58, wt = 0, wn = 0, outputs: 38 x 38 x 512 0.001 BF
62 conv 1024 3 x 3/ 2 38 x 38 x 512 -> 19 x 19 x1024 3.407 BF
63 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
64 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
65 Shortcut Layer: 62, wt = 0, wn = 0, outputs: 19 x 19 x1024 0.000 BF
66 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
67 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
68 Shortcut Layer: 65, wt = 0, wn = 0, outputs: 19 x 19 x1024 0.000 BF
69 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
70 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
71 Shortcut Layer: 68, wt = 0, wn = 0, outputs: 19 x 19 x1024 0.000 BF
72 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
73 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
74 Shortcut Layer: 71, wt = 0, wn = 0, outputs: 19 x 19 x1024 0.000 BF
75 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
76 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
77 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
78 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
79 conv 512 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x 512 0.379 BF
80 conv 1024 3 x 3/ 1 19 x 19 x 512 -> 19 x 19 x1024 3.407 BF
81 conv 1818 1 x 1/ 1 19 x 19 x1024 -> 19 x 19 x1818 1.344 BF
82 yolo
[yolo] params: iou loss: mse (2), iou_norm: 0.75, obj_norm: 1.00, cls_norm: 1.00, delta_norm: 1.00, scale_x_y: 1.00
83 route 79 -> 19 x 19 x 512
84 conv 256 1 x 1/ 1 19 x 19 x 512 -> 19 x 19 x 256 0.095 BF
85 upsample 2x 19 x 19 x 256 -> 38 x 38 x 256
86 route 85 61 -> 38 x 38 x 768
87 conv 256 1 x 1/ 1 38 x 38 x 768 -> 38 x 38 x 256 0.568 BF
88 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
89 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
90 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
91 conv 256 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x 256 0.379 BF
92 conv 512 3 x 3/ 1 38 x 38 x 256 -> 38 x 38 x 512 3.407 BF
93 conv 1818 1 x 1/ 1 38 x 38 x 512 -> 38 x 38 x1818 2.688 BF
94 yolo
[yolo] params: iou loss: mse (2), iou_norm: 0.75, obj_norm: 1.00, cls_norm: 1.00, delta_norm: 1.00, scale_x_y: 1.00
95 route 91 -> 38 x 38 x 256
96 conv 128 1 x 1/ 1 38 x 38 x 256 -> 38 x 38 x 128 0.095 BF
97 upsample 2x 38 x 38 x 128 -> 76 x 76 x 128
98 route 97 36 -> 76 x 76 x 384
99 conv 128 1 x 1/ 1 76 x 76 x 384 -> 76 x 76 x 128 0.568 BF
100 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
101 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
102 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
103 conv 128 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x 128 0.379 BF
104 conv 256 3 x 3/ 1 76 x 76 x 128 -> 76 x 76 x 256 3.407 BF
105 conv 1818 1 x 1/ 1 76 x 76 x 256 -> 76 x 76 x1818 5.376 BF
106 yolo
[yolo] params: iou loss: mse (2), iou_norm: 0.75, obj_norm: 1.00, cls_norm: 1.00, delta_norm: 1.00, scale_x_y: 1.00
Total BFLOPS 148.812
avg_outputs = 1358830
Allocate additional workspace_size = 52.43 MB
Loading weights from yolov3-openimages.weights...
seen 64, trained: 32013 K-images (500 Kilo-batches_64)
Done! Loaded 107 layers from weights-file
Detection layer: 82 - type = 28
Detection layer: 94 - type = 28
Detection layer: 106 - type = 28
./input/test/3bc4e7965d04d91f.jpg: Predicted in 34.763000 milli-seconds.
Person: 28%
Person: 42%
Man: 32%
Clothing: 28%
Person: 49%
Man: 37%
Clothing: 41%
Person: 39%
Person: 53%
Man: 30%
Clothing: 34%
Person: 35%
Person: 55%
Clothing: 29%
상당히 기나 긴 YOLO의 layer들을 거쳐 최종적으로 Person, Man, Clothing object들을 찾아낸 것을 확인할 수 있습니다. 글로만 보면 와닿지 않으니 직접 찾아낸 결과들을 눈으로 확인해볼까요?
매우 빠른 시간 안에 (거의 3~5초?) 저 class들을 찾아주는 것을 알 수 있습니다. YOLO의 장점인 빠른 속도를 느낄 수 있었습니다.
Result
두둥.. submission파일을 열심히 만들고나서야 이걸 깨달았습니다.. 지금 이 대회는 더 이상의 submission을 받지 않고 있네요. 열과 성을 다해 만든 결과물은 아니지만 그래도 제출은 해보고 어느 정도나 rating이 되는지 알고 싶었는데 아쉽습니다 ㅠㅠ
마무리
계속되는 변명같지만 코드 공부가 목적이었기 때문에 빠른 시간 안에 baseline 결과물을 만들어 보았습니다. 아무래도 pre-trained model만 가지고 아무런 tuning없이 진행하다보니 성능이 그렇게 좋지는 않습니다. 실제로 위의 result 결과를 보면 사람처럼 큰 class를 제외하고는 잘 찾지 못한 모습을 알 수 있습니다. (컴퓨터, 의자, 책상 등은 detecting 실패) 실제 모델을 적용할 때에는 더 세세한 tuning과 train이 필요할 것으로 보입니다.
Reference
- Kaggle page: Google AI Open Images - Object Detection Track, 2018
- Data description: Open Images Challenge page
- AlexeyAB Darknet source code: https://github.com/AlexeyAB/darknet
- Darknet 활용 참고 블로그: https://m.blog.naver.com/bigdata-pro/221781790878
- Codes: decision-j gitgub