- Member: 양한수, 김규미
- 실제 cctv 영상파일을 다운받아 사진화 하여 차량인식 테스트(차, 트럭, 버스)
- 프로젝트 진행 과정에서 발생하는 에로사항 관찰 및 해결방식 학습
-
Ai-Hub: 교통문제 해결을 위한 CCTV 교통 영상(고속도로) 파일 사용 (출처)
-
데이터 용량이 너무 커서(약 90GB), 각 train,val 데이터에서 CH01~04번 데이터(약 30GB)까지만 활용하기로 함
- 구글 GCP 사용(PyTorch:2.0, GPU: NVIDIA V100 * 2, CPU Core: 16)
- Ultralytics YOLOv8.0.216
- Python-3.10.13
- 데이터셋 파일 다운받은 후 신규 버킷 생성하여 업로드 진행
- 버킷 생성 시 (멀티리전/단일리전) 중 단일리전 으로 버킷 생성함(다른 리전에 접근할 경우가 없으므로 속도를 위해 선택)
- 대용량 파일이므로 한번에 업로드 중 네트워크 오류 발생시 다시 처음부터 업로드를 해야하는 위험성이 잇음
- 반디집을 이용해 분할 압축 후 버킷에 업로드 하여 테스트 진행 시 재압축하는식으로 하는 방식도 가능(문제 발생시 재업로드 편의성 증가)
- 버킷과 gcp로 실행한 jpyter notebook 연동
- gcsfuse 로 마운트(colab의 드라이브 마운트와 같이 실시간 연동하는 식)
- gsutill 로 접속하여 복사(cp) 하거나 다운로드를 통해 jpyter lab의 local로 옮겨서 사용(원본은 변화안됨, 딴사람의 버킷에 접속하여 다운가능)
- gsutill 로 연동하면 복사/업로드 과정이 gcsfuse 보다 2배 이상 빠름
- 가져온 압축파일 압축풀기
===================================================================
BUCKET_NAME = 'yolostudy'
MOUNT_PATH ='/home/jupyter/dataset'
!gcsfuse --implicit-dirs {BUCKET_NAME} {MOUNT_PATH}
===================================================================
- 여러가지 압축푸는 방식 존재, 분할압축하여 버킷에 업로드 했다면 분할된 압축파일을 다시 재압축 후 unzip 해야함
!zip -s 0 image.zip --out iamge_total.zip
-
unzip [압축파일] -d [압축 풀 경로]
- 압축 푸는 시간이 오래걸림, 약 1시간 30분 이상...
-
현재 컴퓨터의 가용 CPU Core를 전부 사용하여 압축을 푸는 multiprocessing 기능 사용
- 압축푸는 시간 크게 단축됨.
%%time
%cd ~/
import os
import zipfile
import multiprocessing
import concurrent.futures
cpu_num = os.cpu_count() # 현재 pc의 cpu 코어 수 확인
## [라벨]1.수도권영동선 - 16MB = 1.07 s
# zipfilePath = 'highway/Training/bounding_box/[라벨]1.수도권영동선.zip'
# savePath = 'highway/Training/bounding_box/labels_total'
## [원천]1-1.수도권영동선 - 29.8GB = 56min 21s(cpu = 4) => 17min 7s(cpu = 14)
# zipfilePath = 'highway/Training/bounding_box/[원천]1-1.수도권영동선.zip'
# savePath = 'highway/Training/bounding_box/images_total'
## [라벨]1.수도권영동선 - 2.5MB = 6.27 s(cpu = 4) => 463 ms(cpu = 14)
# zipfilePath = 'highway/Validation/bounding_box/[라벨]1.수도권영동선.zip'
# savePath = 'highway/Validation/bounding_box/labels_total'
## [원천]1.수도권영동선 - 9GB = 18min 31s(cpu = 4) => 3min 31s(cpu = 14)
## - 폴더 이름 깨짐 이슈때문에 jupyter local로 압축파일 복사하여 압축풀기 후 mv로 폴더이름 변경 진행함
# zipfilePath = 'highway/val/[원천]1.수도권영동선.zip'
# savePath = 'highway/val/images_total'
def unzip(file):
with lock:
zf.extract(file, path=savePath)
zf = zipfile.ZipFile(zipfilePath)
m = multiprocessing.Manager()
lock = m.Lock() # 여러개의 폴더의 압축을 풀떄 하나의 폴더의 압축을 다 풀때까지 다른작업 중지하기
with concurrent.futures.ProcessPoolExecutor(max_workers=cpu_num-2) as executor:
executor.map(unzip, zf.infolist()) # map = 여러개의 기능을 동시에 실행
===================================================================
- 전처리 중 발생한 이슈 확인 및 해결
- 학습의 사용될 YOLO 모델의 형식대로 데이터 정리가 필요함
- train,val 폴더 아래에 각각 images, labels 폴더 아래에 데이터가 위치해야함
- 기존 구성에서 iamges 폴더 아래에 여러개의 폴더에 나뉘어서 데이터가 위치하였으므로 각각의 폴더 내부의 접근하여 데이터를 옮기는 함수를 작성하여 실행
- 함수 선언(2중 for문형태의 코드를 간편화 하기 위해 내부 for문을 함수로 지정하여 따로 선언)
# images_total 하위의 폴더명 리스트를 전달받은 후,
# 해당 폴더 아래에 접근하여 폴더 내에 존재하는 이미지 파일들을 목적지(dstDir)로 옮기는 함수 선언
# 전달받을 parameter:
## baseDir: /highway/val/images_total/폴더명
## srcFiles: images_total/폴더/ 아래에 있는 이미지 파일들의 리스트
## dstDir: 데이터를 이동시킬 최종 목적지 highway/val/images
def move_files(baseDir, srcFiles, dstDir):
for srcFile in srcFiles:
srcFilePath = os.path.join(baseDir, srcFile) # 각각의 이미지데이터(png,jpg)의 전체 경로 저장
#print(srcFilePath)
#print(srcFilePath, dstDir)
shutil.move(srcFilePath, dstDir) # 경로가 저장된 각각의 이미지파일을 롬기는 명령 실행
- 함수 호출(images 폴더 아래에 각 폴더에 접근할떄마다 해당 폴더 아래의 데이터를 옮기는 코드 실행)
%%time
## image파일만 옮기기 실행, label 데이터는 xml파일을 txt파일로 변환하면서 저장 예정
## images_total 아래에 있는 각각의 폴더에 있는 png 파일을 전부 추출해 iamges폴더로 옮기기
## 초기 경로 지정
# val/iamges_total 데이터 옮기기 = 총 3333개
# srcBaseDir = os.path.join(dataPath, "highway/val/images_total")
# destPath = os.path.join(dataPath,"highway/val/images")
# tarin/images_total 데이터 옮기기 = 총 24102개
# srcBaseDir = os.path.join(dataPath, "highway/train/images_total")
# destPath = os.path.join(dataPath,"highway/train/images")
## =========== txt_total에 있는 txt 파일을 옮기기 전에 xnkTotxt 변환작업을 먼저 수행해야 함 ============ ##
## =========== 레이블xml2txt변환하기.ipynb 파일 참조 ============ ##
# val/txt_total 데이터 옮기기 = 총 3334 개
# srcBaseDir = os.path.join(dataPath, "highway/val/txt_total")
# destPath = os.path.join(dataPath,"highway/val/labels")
# train/txt_total 데이터 옮기기 = 총 24104개
#srcBaseDir = os.path.join(dataPath, "highway/train/txt_total")
#destPath = os.path.join(dataPath,"highway/train/labels")
# images_total 하위의 디렉토리 목록을 리스트 데이터로 생성
dirNames = os.listdir(srcBaseDir)
# 총 이동시킨 file의 개수를 확인하기 위해 사용
fileNumCounter=0
# images_total 하위의 디렉토리 목록 리스트를 하나씩 꺼내어 반복문 실행
for dirName in dirNames:
srcBasePath = os.path.join(srcBaseDir,dirName) # images_total/dirname경로 저장
fileNames = os.listdir(srcBasePath) # images_total/dirname경로 를 사용하여 images_total/dirname 폴더 내부의 이미지 파일목록을 리스트로 저장
print("dir:{}, fileNum:{}".format(dirName, len(fileNames))) # 폴더 이름과 각 폴더별 내부 이미지 데이터 개수 확인을 위해 출력
fileNumCounter+=len(fileNames) # 각 폴더의 내부 데이터 개수를 합산하기 위해 저장
# 각 폴더명에 접근하여 내부 데이터를 옮기는 함수 호출
move_files(srcBasePath, fileNames, destPath)
#print(fileNames)
# 총 옮겨진 파일 개수 확인
print("fileNumCounter:{}".format(fileNumCounter))
- 실행 결과
dir:Suwon_CH02_20200720_2130_MON_9m_NH_highway_TW5_sunny_FHD, fileNum:150
dir:Suwon_CH04_20201012_1838_MON_9m_RH_highway_OW5_sunny_FHD, fileNum:150
dir:Suwon_CH04_20200721_1830_TUE_9m_RH_highway_OW5_sunny_FHD, fileNum:150
dir:Suwon_CH01_20201012_1723_MON_9m_RH_highway_TW5_sunny_FHD, fileNum:150
dir:Suwon_CH03_20201012_1933_MON_9m_RH_highway_OW5_sunny_FHD, fileNum:149
dir:Suwon_CH02_20200722_1730_WED_9m_NH_highway_TW5_sunny_FHD, fileNum:50
dir:Suwon_CH01_20200721_1700_TUE_9m_RH_highway_TW5_sunny_FHD, fileNum:150
dir:Suwon_CH04_20200720_1830_MON_9m_NH_highway_OW5_sunny_FHD, fileNum:150
dir:Suwon_CH04_20201010_1818_SAT_9m_RH_highway_OW5_sunny_FHD, fileNum:150
dir:Suwon_CH02_20200721_2030_TUE_9m_NH_highway_TW5_sunny_FHD, fileNum:100
dir:Suwon_CH03_20201011_1742_SUN_9m_RH_highway_OW5_sunny_FHD, fileNum:150
dir:Suwon_CH04_20200722_1600_WED_9m_NH_highway_OW5_rainy_FHD, fileNum:625
dir:Suwon_CH03_20200721_2000_TUE_9m_NH_highway_OW5_sunny_FHD, fileNum:100
dir:Suwon_CH03_20200722_1700_WED_9m_NH_highway_OW5_sunny_FHD, fileNum:100
dir:Suwon_CH01_20200722_1930_WED_9m_NH_highway_TW5_sunny_FHD, fileNum:50
dir:Suwon_CH03_20200720_2030_MON_9m_NH_highway_OW5_sunny_FHD, fileNum:150
dir:Suwon_CH01_20200722_1430_WED_9m_NH_highway_TW5_rainy_FHD, fileNum:50
dir:Suwon_CH02_20201011_1806_SUN_9m_RH_highway_TW5_sunny_FHD, fileNum:150
dir:Suwon_CH01_20200720_1830_MON_9m_RH_highway_TW5_sunny_FHD, fileNum:150
dir:Suwon_CH01_20201213_1200_SUN_9m_NH_highway_TW5_snow_FHD, fileNum:230
dir:Suwon_CH02_20201213_0933_SUN_9m_NH_highway_TW5_snow_FHD, fileNum:230
fileNumCounter:3334
CPU times: user 50.2 ms, sys: 66.5 ms, total: 117 ms
Wall time: 141 ms
- 해당 작업 이후 데이터가 없는 빈 파일들 삭제 진행
===================================================================
- txt파일명이 잘못된 것 수정
- train 파일의 images와 labels의 파일명이 일치하지 않는것 약 150개 발견
- 이미지 이름은 'Suwon_CH02_20200721_2130_TUE_9m_NH_highway_TW5_sunny_FHD.png'
- 텍스트 파일은 'Suwon_CH02_20200721_2130_TUE_9m_NH_highway_TW5_sunny_FHD 001.txt'
- txt파일명에 언더바 대신 공백이 있는 것 확인
- 아래 코드로 txt 파일명에 언더바(_) 추가 하여 해결
falseLabels = glob('/home/jupyter/highway/train/labels/Suwon_CH02_20200721_2130*.txt') for falseLabel in falseLabels: newLabel = falseLabel.replace(' ','_') print(newLabel) os.rename(falseLabel, newLabel)
===================================================================
- txt 파일명 수정 후에도 여전히 iamges, rabels 데이터 개수 불일치 확인
- labels 폴더에 txt파일이 3개 더 많은것을 확인한 후 결측치인지 확인하기
- labels 폴더에서 아래 3개의 파일이 images 파일에 없는것을 확인
- Suwon_CH02_20200721_1530_TUE_9m_NH_highway_TW5_sunny_FHD_069.txt
- uwon_CH03_20200722_1900_WED_9m_NH_highway_OW5_sunny_FHD_051.txt
- Suwon_CH03_20200722_1900_WED_9m_NH_highway_OW5_sunny_FHD_052.txt
- 원본 압축파일에서 확인해본 결과 해당 파일이 없는것을 확인
- 결측치로 판단하여 labels 폴더에서 해당 텍스트 파일 삭제하는 코드 작성하여 실행
# split_labelfiles 에는 labels폴더 하위의 데이터리스트들의 이름이 담겨잇음
true_count=0
false_count=0
for label in split_labelfiles:
if label in split_imagefiles:
true_count+=1
else:
false_count+=1
print('불일치 파일: {}'.format(label))
falsefilePath = os.path.join(labelfullPath,label + '.txt')
#print(falsefilePath)
#해당파일 삭제
os.remove(falsefilePath)
print('파일 삭제 완료!!!')
print(f'true_count:{true_count}')
print(f'false_count:{false_count}')
-
val 데이터 폴더 아래에서도 한개의 label 파일이 많은것을 확인하여 차집합 계산식을 활용해 검출하여 삭제함
- 결측 파일 검출
s1 = set(split_imagefiles) len(s1) s2 = set(split_labelfiles) len(s2) s2-s1
{'Suwon_CH02_20200721_2030_TUE_9m_NH_highway_TW5_sunny_FHD_001'}
- 결측 파일 삭제
os.remove('/home/jupyter/highway/val/labels/Suwon_CH02_20200721_2030_TUE_9m_NH_highway_TW5_sunny_FHD_001.txt')
===================================================================
- 'Suwon_CH02_20200721_2130_TUE_9m_NH_highway_TW5_sunny_FHD_096.png'
- highway/train/images/Suwon_CH02_20200722_1530_WED_9m_NH_highway_TW5_rainy_FHD_048.png 파일을 확인해보니 0KB인것 확인함
- 압축 풀기 전 원본 데이터 확인해보니 해당 파일 존재함
- 압축 푸는과정에서 손상난것으로 추정
- 손상파일 삭제 후, 원본 압축파일에서 해당 png 파일만 추출해서 업로드하는 식으로 파일 교체함
===================================================================
- 1차 테스트:nano model test - epochs=75 설정
!yolo task=detect mode=train model=yolov8n.pt data={dataYaml} epochs=75 imgsz=640 plots=True batch=16 device=0,1 cache=True
- 2차 테스트: nano model test - epochs=150 설정
!yolo task=detect mode=train model=yolov8n.pt data={dataYaml} epochs=150 imgsz=640 plots=True batch=16 device=0,1 cache=True