Hồ Chí Minh, 16-08-2023
[Võ Duy Nguyên](https://nguyenvd-uit.github.io/), [Lê Hữu Độ](https://github.com/Ngyyen), [UIT-Together Research Group](https://uit-together.github.io/)
# DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection
## Mục Lục
[TOC]
## Step 1. Cài đặt môi trường
### Step 1.1. Tạo môi trường anaconda
Đặt tên theo cú pháp: Tên viết tắt của họ và chữ lót
VD: Le Huu Do -> Dolh
```gherkin=
conda create --name UITTogether python=3.10 -y
```
Hình ảnh sau khi tạo môi trường
![](https://hackmd.io/_uploads/BkWrrM923.png)
### Step 1.2. Kích hoạt môi trường vừa tạo
```gherkin=
conda activate UITTogether
```
![](https://hackmd.io/_uploads/r1HeBzq33.png)
### Step 1.3. Cài đặt PyTorch trên GPU platforms
```gherkin=
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
```
Hình ảnh sau khi cài đặt thành công
![](https://hackmd.io/_uploads/rJbZuzc3h.png)
## Step 2. Cài đặt detrex và detectron2
Truy cập vào thư mục LuuTru
VD: /home/cvpr2023/LuuTru/
```gherkin=
cd LuuTru/
```
Tạo thư mục tương ứng với tên môi trường bên trên
![](https://hackmd.io/_uploads/ByVGkxKih.png)
```gherkin=
cd UITTogether/
```
### Step 2.1. Cài đặt detrex
Tại thư mục này thực hiện clone và cài đặt detrex
```gherkin=
git clone https://github.com/IDEA-Research/detrex.git
cd detrex
```
Hình ảnh sau khi clone thành công
![](https://hackmd.io/_uploads/HyAhBz9hn.png)
![](https://hackmd.io/_uploads/r1VN8Mq22.png)
### Step 2.2. Khởi tạo submodule detectron2
```gherkin=
git submodule init
git submodule update
```
Hình ảnh sau khi khởi tạo thành công
![](https://hackmd.io/_uploads/ByQPdfqhn.png)
### Step 2.3. Cài đặt detectron2
```gherkin=
python -m pip install -e detectron2
```
Hình ảnh sau khi cài đặt thành công
![](https://hackmd.io/_uploads/rJxTYG5n2.png)
### Step 2.4. Build một phiên bản chỉnh sửa được của detrex
```gherkin=
pip install -e .
```
Hình ảnh sau khi build thành công
![](https://hackmd.io/_uploads/HJ-Y9Gqnh.png)
## Step 3. Verify the installation
### Step 3.1. Tải pretrained model và ảnh demo
```gherkin=
# download pretrained DINO model
wget https://github.com/IDEA-Research/detrex-storage/releases/download/v0.2.1/dino_r50_4scale_12ep.pth
# download the demo image
wget https://github.com/IDEA-Research/detrex-storage/releases/download/v0.2.1/idea.jpg
```
Hình ảnh sau khi tải thành công pretrained model và ảnh demo
![](https://hackmd.io/_uploads/ryz0-Eonn.png)
### Step 3.2. Chạy inference pretrained model trên ảnh demo.
```gherkin=
python demo/demo.py --config-file ./projects/dino/configs/dino-resnet/dino_r50_4scale_12ep.py \
--input "./idea.jpg" \
--output "./demo_output.jpg" \
--opts train.init_checkpoint="./dino_r50_4scale_12ep.pth"
```
Kết quả được lưu trong file demo_output.jpg
Vd: /home/cvpr2023/LuuTru/UITTogether/detrex/demo_output.jpg
![](https://hackmd.io/_uploads/H1VyJQc23.jpg)
### Step 3.3. Chạy đánh giá pretrained model trên bộ dữ liệu COCO 2017.
Bộ dữ liệu COCO 2017 đã được tải về từ trước và lưu ở địa chỉ:
/home/cvpr2023/LuuTru/dataset/coco/
![](https://hackmd.io/_uploads/rk5z-Q9hh.png)
Câu lệnh thực hiện:
```gherkin=
export DETECTRON2_DATASETS=/home/cvpr2023/LuuTru/dataset/
export CUDA_VISIBLE_DEVICES=0,1 python projects/dino/train_net.py \
--config-file ./projects/dino/configs/dino-resnet/dino_r50_4scale_12ep.py \
--eval-only train.init_checkpoint="./dino_r50_4scale_12ep.pth"
```
Kết quả chạy đánh giá sẽ xấp xỉ với các giá trị trong bảng dưới đây:
![](https://hackmd.io/_uploads/r15pAEinn.png)
## Step 4. Train model trên các bộ dữ liệu theo format COCO
### Step 4.1. Train trên bộ dữ liệu COCO 2017 đã được tổ chức sẵn theo format COCO và được bộ công cụ chuẩn bị sẵn cấu hình
Câu lệnh thực hiện:
```gherkin=
python projects/dino/train_net.py \
--config-file ./projects/dino/configs/dino-resnet/dino_r50_4scale_12ep.py
```
Nếu bị lỗi "CUDA out of memory" thì có thể vào file config tại địa chỉ "/projects/dino/configs/dino-resnet/dino_r50_4scale_12ep.py" và sửa biến dataloader.train.total_batch_size thành 1.
Màn hình hiện ra những dòng thông báo như dưới đây tức là đã bắt đầu train được
![](https://hackmd.io/_uploads/Bk1DCbj2h.png)
Trong quá trình train, các file checkpoint sẽ được lưu tại địa chỉ output/dino_r50_4scale_12ep/
![](https://hackmd.io/_uploads/B1VIZwo22.png)
### Step 4.2. Train trên một bộ dữ liệu mới tùy chọn
#### Step 4.2.1. Chuẩn bị bộ dữ liệu và tiền xử lý
Trong ví dụ này, chúng ta sẽ chọn bộ dữ liệu VisDrone 2019 để train model mới dùng phương pháp DINO.
Sau khi tải bộ dữ liệu về, bước đầu tiên là phải chuyển đổi các file annotation theo format của COCO. Mỗi bộ dữ liệu có một format annotation ban đầu khác nhau, các bạn cần tham khảo thêm trên mạng để tìm cách chuyển, có thể sử dụng code có sẵn trên github hoặc các tool hỗ trợ như roboflow.
Sau khi đã chuyển đổi bộ dữ liệu theo format của COCO, các bạn tải lên bộ dữ liệu tại địa chỉ: dataset/VisDrone/cocoVisdrone/
![](https://hackmd.io/_uploads/r1BmnQs33.png)
#### Step 4.2.2. Chuẩn bị file config
Đến địa chỉ "projects/dino/configs/dino-resnet/" và tạo một file config có tên "visdrone_dino_r50_4scale_1ep" với nội dung như sau:
```gherkin=
from detrex.config import get_config
from ..models.dino_r50 import model
# get default config
dataloader = get_config("common/data/coco_detr.py").dataloader
optimizer = get_config("common/optim.py").AdamW
lr_multiplier = get_config("common/coco_schedule.py").lr_multiplier_12ep
train = get_config("common/train.py").train
# modify training config
train.init_checkpoint = "https://github.com/IDEA-Research/detrex-storage/releases/download/v0.1.1/dino_r50_4scale_24ep.pth"
train.output_dir = "./output/visdrone_dino_r50_4scale_1ep"
# max training iterations
train.max_iter = 6500
train.eval_period = 6500
train.log_period = 100
train.checkpointer.period = 3500
# gradient clipping for training
train.clip_grad.enabled = True
train.clip_grad.params.max_norm = 0.1
train.clip_grad.params.norm_type = 2
# set training devices
train.device = "cuda"
model.device = train.device
# modify optimizer config
optimizer.lr = 1e-4
optimizer.betas = (0.9, 0.999)
optimizer.weight_decay = 1e-4
optimizer.params.lr_factor_func = lambda module_name: 0.1 if "backbone" in module_name else 1
# modify dataloader config
dataloader.train.num_workers = 4
# please notice that this is total batch size.
# surpose you're using 4 gpus for training and the batch size for
# each gpu is 16/4 = 4
dataloader.train.total_batch_size = 1
# dump the testing results into output_dir for visualization
dataloader.evaluator.output_dir = train.output_dir
#User change
#import library to change num_class
import itertools
from omegaconf import OmegaConf
import detectron2.data.transforms as T
from detectron2.config import LazyCall as L
from detectron2.data import (
build_detection_test_loader,
build_detection_train_loader,
get_detection_dataset_dicts,
)
#import library to register new dataset
from detectron2.data.datasets import register_coco_instances
from detectron2.evaluation import COCOEvaluator
from detrex.data import DetrDatasetMapper
dataloader = OmegaConf.create()
#register new dataset
register_coco_instances("VisDrone_train", {}, "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/annotations_cu/train.json", "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/train/")
register_coco_instances("VisDrone_test", {}, "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/annotations_cu/test.json", "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/test/")
register_coco_instances("VisDrone_val", {}, "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/annotations_cu/val.json", "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/val/")
dataloader.train = L(build_detection_train_loader)(
dataset=L(get_detection_dataset_dicts)(names="VisDrone_train"),
mapper=L(DetrDatasetMapper)(
augmentation=[
L(T.RandomFlip)(),
L(T.ResizeShortestEdge)(
short_edge_length=(480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800),
max_size=1333,
sample_style="choice",
),
],
augmentation_with_crop=[
L(T.RandomFlip)(),
L(T.ResizeShortestEdge)(
short_edge_length=(400, 500, 600),
sample_style="choice",
),
L(T.RandomCrop)(
crop_type="absolute_range",
crop_size=(384, 600),
),
L(T.ResizeShortestEdge)(
short_edge_length=(480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800),
max_size=1333,
sample_style="choice",
),
],
is_train=True,
mask_on=False,
img_format="RGB",
),
total_batch_size=1,
num_workers=4,
)
dataloader.test = L(build_detection_test_loader)(
dataset=L(get_detection_dataset_dicts)(names="VisDrone_test", filter_empty=False),
mapper=L(DetrDatasetMapper)(
augmentation=[
L(T.ResizeShortestEdge)(
short_edge_length=800,
max_size=1333,
),
],
augmentation_with_crop=None,
is_train=False,
mask_on=False,
img_format="RGB",
),
num_workers=4,
)
dataloader.evaluator = L(COCOEvaluator)(
dataset_name="${..test.dataset.names}",
)
#change model num class
from projects.dino.modeling import (
DINO,
DINOTransformerEncoder,
DINOTransformerDecoder,
DINOTransformer,
DINOCriterion,
)
from detrex.modeling.matcher import HungarianMatcher
model.num_classes=12
model.criterion=L(DINOCriterion)(
num_classes=12,
matcher=L(HungarianMatcher)(
cost_class=2.0,
cost_bbox=5.0,
cost_giou=2.0,
cost_class_type="focal_loss_cost",
alpha=0.25,
gamma=2.0,
),
weight_dict={
"loss_class": 1,
"loss_bbox": 5.0,
"loss_giou": 2.0,
"loss_class_dn": 1,
"loss_bbox_dn": 5.0,
"loss_giou_dn": 2.0,
},
loss_class_type="focal_loss",
alpha=0.25,
gamma=2.0,
two_stage_binary_cls=False,
)
#Defining classes
from detectron2.data import MetadataCatalog
MetadataCatalog.get("VisDrone").thing_classes = ['ignored regions', 'pedestrian', 'people', 'bicycle', 'car', 'van', 'truck', 'tricycle', 'awning-tricycle', 'bus', 'motor', 'others']
```
Các thay đổi so với config mặc định:
* Các thông số: train.max_iter, train.eval_period, train.log_period, train.checkpointer.period, model.num_classes, num_classes,...
![](https://hackmd.io/_uploads/S1r7zyG63.png)
* Tên bộ dữ liệu được thay đổi tương ứng với bộ dữ liệu mà chúng ta chạy thực nghiệm, VD ở đây là VisDrone.
* Link init_checkpoint được lấy trên Model Zoo của github detrex.
![](https://hackmd.io/_uploads/S17Zfkfp3.png)
* Sử dụng metadata để định nghĩa các class.
![](https://hackmd.io/_uploads/rJhrfJG63.png)
#### 4.2.3 Training với file config đã chuẩn bị.
Thực hiện các dòng lệnh sau để bắt đầu train:
```gherkin=
export DETECTRON2_DATASETS=/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/
python projects/dino/train_net.py \
--config-file ./projects/dino/configs/dino-resnet/visdrone_dino_r50_4scale_1ep.py
```
Sau khi train xong, dùng file checkpoint model_final.pth được lưu ở địa chỉ "./output/visdrone_dino_r50_4scale_1ep" để chạy đánh giá.
![](https://hackmd.io/_uploads/rkdimws2n.png)
```gherkin=
python tools/train_net.py --config-file "/projects/dino/configs/dino-resnet/visdrone_dino_r50_4scale_1ep.py" \
--eval-only \
train.init_checkpoint="output/visdrone_dino_r50_4scale_1ep/model_final.pth"
```
#### 4.2.4 Chạy inference model vừa mới train được trên ảnh demo.
Chạy câu lệnh sau:
```gherkin=
python demo/demo.py --config-file projects/dino/configs/dino-resnet/visdrone_dino_r50_4scale_1ep.py \
--input "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/train/0000309_00801_d_0000337.jpg" \
--output demo_output.jpg \
--opts train.init_checkpoint="./output/visdrone_dino_r50_4scale_1ep/model_final.pth"
```
Tuy nhiên, mặc định demo.py sẽ visualize model của chúng ta theo cách đánh số có nhãn trên tập coco_2017_val.
![](https://hackmd.io/_uploads/S1cu-4Anh.png)
Sẽ xảy ra hiện tượng category_id trên tập dữ liệu mới nhưng label của tập coco2017.
Để khắc phục hiện tượng trên chúng ta cần cho demo.py biết tên của tập dữ liệu mới đã được đăng ký ở trên bằng cách thêm tham số đầu vào
```gherkin=
--metadata_dataset "dataset_name"
```
Ví dụ chúng ta sử dụng bộ dữ liệu VisDrone thì câu lệnh tương ứng sẽ như sau:
```gherkin=
python demo/demo.py --config-file projects/dino/configs/dino-resnet/visdrone_dino_r50_4scale_1ep.py \
--input "/home/cvpr2023/LuuTru/dataset/VisDrone/cocoVisdrone/train/0000309_00801_d_0000337.jpg" \
--output demo_output.jpg \
--metadata_dataset "VisDrone" \
--opts train.init_checkpoint="./output/visdrone_dino_r50_4scale_1ep/model_final.pth"
```
Kết quả ta được output như sau: