yolov5 모델로 보행자 사람 자전거만 탐지하기/검증파일 실행해보기.
transfer learning을 공부하기 위해서 yolo를 공부하고 있음.
https://freddiekim.tistory.com/16
Transfer Learning Tutorial(전이학습)
전이학습이 어떤 방식으로 이루어 지는지 궁금해서 아래 예제로 공부하면서 코드를 작성해봤다. 재사용을 위해서 리팩토링을 했므으로 같은 함수이다. 출저 : https://9bow.github.io/PyTorch-tutorials-kr-0
freddiekim.tistory.com
위 예에서 난 resnet을 이용해서 250장 정도 train데이터를 이용해서 90%이상 벌과 개미를 구분하는 네트워크를 만들었음.
이번에는 coco데이터로 학습되어 있는 네트워크를 이용해서
난 사람,자전거,보행자만 탐지하고 싶고 적은 장수를 학습하고 괜찮은 결과를 얻고 싶음.
그렇게 하기위해서는 노가다를 해야함.
coco데이터는 출력이 80개임. 그래서 3개로 줄일 생각임.
# 0: --> 0: person
# 1: --> 1: bicycle/ 3: motorcycle
# 2: --> 2: car/ 5: bus/ 7: truck
이렇게 하기 위해서는 우선 coco data를 받야함.
yolov5/data/scripts/get_coco.sh를 사용하면 쉽게 다운 받을 수 있음.
코드를 보면 val 이 5천장으로 가장 작음.
#!/bin/bash
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# Download COCO 2017 dataset http://cocodataset.org
# Example usage: bash data/scripts/get_coco.sh
# parent
# ├── yolov5
# └── datasets
# └── coco ← downloads here
# Arguments (optional) Usage: bash data/scripts/get_coco.sh --train --val --test --segments
if [ "$#" -gt 0 ]; then
for opt in "$@"; do
case "${opt}" in
--train) train=true ;;
--val) val=true ;;
--test) test=true ;;
--segments) segments=true ;;
esac
done
else
train=false
val=false
test=false
segments=false
fi
# Download/unzip labels
d='../datasets' # unzip directory
url=https://github.com/ultralytics/yolov5/releases/download/v1.0/
if [ "$segments" == "true" ]; then
f='coco2017labels-segments.zip' # 168 MB
else
f='coco2017labels.zip' # 46 MB
fi
echo 'Downloading' $url$f ' ...'
curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f &
# Download/unzip images
d='../datasets/coco/images' # unzip directory
url=http://images.cocodataset.org/zips/
if [ "$train" == "true" ]; then
f='train2017.zip' # 19G, 118k images
echo 'Downloading' $url$f '...'
curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f &
fi
if [ "$val" == "true" ]; then
f='val2017.zip' # 1G, 5k images
echo 'Downloading' $url$f '...'
curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f &
fi
if [ "$test" == "true" ]; then
f='test2017.zip' # 7G, 41k images (optional)
echo 'Downloading' $url$f '...'
curl -L $url$f -o $f -# && unzip -q $f -d $d && rm $f &
fi
wait # finish background tasks
위 처럼 수정 후 bash get_coco.sh --val 치면 데이터가 다운이됨.
이제 데이터를 가져와서 5000장을 다 학습하기에 내 컴퓨터 성능이 좋지도 않고 시간이 많이 걸리므로 500장만 학습하기로 한다.
from glob import glob
import os
import shutil
def write_line(f,class_id,idx):
cnt = 0
for str in class_id:
if cnt > 0:
f.write(' '+str)
else:
f.write(idx)
cnt += 1
# 이미지들의 주소 리스트로 만들어줌
train_img_list = glob('./yolov5/data/datasets/coco/images/val2017/*.jpg')
valid_img_list = glob('./yolov5/data/datasets/coco/labels/val2017/*.txt')
# 폴더 생성
root_labels_path = "./yolov5/data/datasets/coco/labels/val2017/"
train_images = './yolov5/data/datasets/coco512/images/train2017/'
train_labels = './yolov5/data/datasets/coco512/labels/train2017/'
val_images = './yolov5/data/datasets/coco512/images/val2017/'
val_labels = './yolov5/data/datasets/coco512/labels/val2017/'
if not os.path.isdir(train_images):
os.makedirs(train_images)
if not os.path.isdir(train_labels):
os.makedirs(train_labels)
if not os.path.isdir(val_images):
os.makedirs(val_images)
if not os.path.isdir(val_labels):
os.makedirs(val_labels)
train_cnt = 0
val_cnt = 0
tht_cnt = 512
for f in train_img_list:
name = os.path.basename(f)
if train_cnt < tht_cnt:
label_name = name.split('.')[0] + '.txt'
full_name = root_labels_path + label_name
if os.path.isfile(full_name):
shutil.copyfile(f,train_images+('%06d' % train_cnt)+'.jpg')
f2 = open(full_name,'r')
lines = f2.readlines()
f2.close()
en_write = False
with open(train_labels+('%06d' % train_cnt)+'.txt','w') as f:
for line in lines:
class_id = line.split(' ')
if class_id[0] == '0':
write_line(f,class_id,'0')
en_write = True
elif class_id[0] == '1' or class_id[0] == '3':
write_line(f,class_id,'1')
en_write = True
elif class_id[0] == '2' or class_id[0] == '5' or class_id[0] == '5':
write_line(f,class_id,'2')
en_write = True
if train_cnt%50 == 0:
if en_write == False:
train_cnt += 1
else:
if en_write == True:
train_cnt += 1
elif val_cnt < tht_cnt:
label_name = name.split('.')[0] + '.txt'
full_name = root_labels_path + label_name
if os.path.isfile(full_name):
shutil.copyfile(f,val_images+('%06d' % val_cnt)+'.jpg')
# shutil.copyfile(full_name,val_labels+('%06d' % val_cnt)+'.txt')
f2 = open(full_name,'r')
lines = f2.readlines()
f2.close()
en_write = False
with open(val_labels+('%06d' % val_cnt)+'.txt','w') as f:
for line in lines:
class_id = line.split(' ')
if class_id[0] == '0':
write_line(f,class_id,'0')
en_write = True
elif class_id[0] == '1' or class_id[0] == '3':
write_line(f,class_id,'1')
en_write = True
elif class_id[0] == '2' or class_id[0] == '5' or class_id[0] == '5':
write_line(f,class_id,'2')
en_write = True
if val_cnt%50 == 0:
if en_write == False:
val_cnt += 1
else:
if en_write == True:
val_cnt += 1
위 코드를 실행하면
훈련용 512 장 검증용 512장이 만들어 짐.
그리고 클래스 80개는 3개로 줄여서 할당됨.
coco512.yaml 만들기 학습을 하기위해서 yaml 파일을 수정해야함.
다운을 다 받았으므로 주석처리하고 아래 처럼 수정해서 새롭게 만듬.
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# COCO128 dataset https://www.kaggle.com/ultralytics/coco128 (first 128 images from COCO train2017) by Ultralytics
# Example usage: python train.py --data coco128.yaml
# parent
# ├── yolov5
# └── datasets
# └── coco128 ← downloads here (7 MB)
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ./data/datasets/coco512 # dataset root dir
train: images/train2017 # train images (relative to 'path') 128 images
val: images/val2017 # val images (relative to 'path') 128 images
test: # test images (optional)
# Classes
names:
0: person
1: bicycle
2: car
# Download script/URL (optional)
# download: https://ultralytics.com/assets/coco128.zip
아래처럼 실행함. 이렇게 하는 이유는 디버깅을 할 수 있음.
import torch
from torchsummary import summary as summary
import subprocess
import os
os.chdir('./yolov5/')
cmd = "python train.py --data coco512.yaml --weights yolov5s.pt --img 640 --epochs 3"
print(cmd.split(' '))
subprocess.run(cmd.split(' '))
이렇게 실행을 다하면 결과가
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
0/2 0G 0.1053 0.05411 0.03405 138 640: 100%|██████████| 32/32 [03:50<00:00, 7.21s/it]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [01:25<00:00, 5.35s/it]
all 512 2238 0.138 0.118 0.0592 0.0181
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
1/2 0G 0.08058 0.05103 0.01677 177 640: 100%|██████████| 32/32 [03:51<00:00, 7.24s/it]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [01:09<00:00, 4.33s/it]
all 512 2238 0.797 0.148 0.176 0.061
Epoch GPU_mem box_loss obj_loss cls_loss Instances Size
2/2 0G 0.06951 0.04642 0.01233 121 640: 100%|██████████| 32/32 [03:40<00:00, 6.90s/it]
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:58<00:00, 3.63s/it]
all 512 2238 0.579 0.332 0.297 0.121
3 epochs completed in 0.250 hours.
Optimizer stripped from runs/train/exp7/weights/last.pt, 14.4MB
Optimizer stripped from runs/train/exp7/weights/best.pt, 14.4MB
Validating runs/train/exp7/weights/best.pt...
Fusing layers...
Model summary: 157 layers, 7018216 parameters, 0 gradients, 15.8 GFLOPs
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 16/16 [00:55<00:00, 3.45s/it]
all 512 2238 0.582 0.328 0.297 0.121
person 512 1760 0.195 0.71 0.449 0.203
bicycle 512 123 1 0 0.156 0.067
car 512 355 0.55 0.273 0.285 0.0929
데이터도 적게 사용하고 epoch도 3번밖에 안해서 성능이 좋지 않지않게 나옴.
참고로 freeze기능을 이용해서 weight고정시키고 뒷단만 학습했지만 결과는 많이 좋아지지 않았음.
그리고
yolov5/runs/train 에 순차적으로 저장됨을 확인 할 수 있음.
다음장에서 validation 파일을 실행해보면서 얼마나 fine tunning이 잘되었는지 확인해보자!
결론은 학습을 많이 안해서 잘안됨. 그러나 쉬운 예는 아래처럼 어느정도 됨.
![]() |
![]() |
그래도 쉬운 시나리오는 잘됨.
마지막으로 그냥 검증 파일을 실행해보자.
자동화가 잘되어 있다.
아래코드를 치면 데이터도 자동으로 받고 실행한다.
python val.py --weights yolov5s.pt --data coco128.yaml --img 640
아래처럼 결과가 나오는것을 볼 수 있다.
즉 학습된 모델로 80개 클래스 출력 128개 데이터를 검증한것이다.
Class Images Instances P R mAP50 mAP50-95: 100%|██████████| 4/4 [00:17<00:00, 4.38s/it]
all 128 929 0.709 0.634 0.713 0.475
Speed: 1.8ms pre-process, 123.0ms inference, 3.9ms NMS per image at shape (32, 3, 640, 640)
128개 이미지에서 929개 탐지된 물품이 있고 그중 0.709 즉 70%정도 탐지했다는 의미이다.
여기서 mAP50 즉50%이상 겹쳐있는 items이 71.3%라는 의미이다.
만일 수동을로 데이터를 받고 싶으면 아래처럼 치면된다.
cd yolov5/data/scripts
bash get_coco128.sh
data/datasets/coco128 폴더가 생성되고 거기에 이미지와 정답지가 같이 다운 되어 있음.
당연한 얘기지만
추가적으로 coco데이터는 80개 클래스 출력이고 내가 수정한 데이터는 3개 클래스 출력이라서 단위가 맞지 않아서 validation이 되지 않는다. 모델을 fine tunning 으로 수정 후 해야함.
resnet 으로 공부할때는 바로바로 이해가 되었는데 yolo는 추상화가 많이되어 있어서 힘들었음.