■ jetson nano 보드에서 object detection
젯슨나노 보드에서 딥러닝을 사용할 수 있도록 NVIDIA에서 깃허브에 정리해둔 코드가 있다. 깃허브에 있는 라이브러리를 가지고 훈련 및 테스트를 진행해 보겠다. 나의 데이터는 이전에 pytorch object detection에서 사용했던 custom robot 데이터를 가지고 해보겠다.
■ github에 있는 파일 기능 분석
우선 레포지토리를 가보면 이런식으로 되어있는데 python 코드를 쓸것이므로 python 폴더를 들어간다.
들어가서보면
이런식으로 되어있는데 training 할 수있는 폴더가 보인다.
training 은 총 분류, 객체검출, 객체분할 이렇게 3가지 가능한것으로 보인다.
그중에서 나는 detection을 해볼것이므로 detection으로 들어가서
이런식으로 나오게 되는데 pytorch-ssd라는 이름으로 나오게 된다. pytorch-ssd라는 이름이 생소해서 찾아보니
pytorch 전용 오픈소스로서 객체탐지 작업을 위한 신경망 구조중 하나라고 나온다. ssd는 빠른속도와 높은 정확도를 제공한다고 나와있다. 빠르고 정확한 객체 탐지를 원하므로 이 신경망을 가지고 훈련해보겠다.
■ train하는 부분 코드 분석 및 데이터 전처리
가장 처음으로 봐야할 부분이 이 train_ssd.py코드에선 데이터셋 포멧을 무엇으로 했는지가 중요하다.
깃허브에 설명을 보면 open images 데이터셋과 Pascal VOC 포멧으로 retraining했다고 나와있다. 그래서 나의 데이터셋도 이 둘중 하나의 포멧으로 맞춰줘야한다는것을 알았고 나는 이전에 사용했던 데이터셋 포멧이 coco포멧이였으므로 훈련이 불가능할것으로 판단하고 Pascal VOC로 바꿔주기로 했다.
labelme 깃허브에 가보면 json파일을 Pascal VOC로 바꿔주는 툴이 있었다.(coco만 해봄) 그것으로 나의 데이터셋 포멧을 Pascal VOC로 바꿔주었다.
근데 Pascal VOC는 내가 텍스트 파일로 train데이터와 test 데이터를 분리 시켜줘야한다.
이렇게 파일명을 텍스트 파일에 넣어주면 끝이다.
■ 훈련가능한 모델 확인
그 다음으론 이 훈련 코드에서 어떤 모델이 훈련가능한지 살펴 보겠다.
모델 구성이 거의다 모바일넷으로 되어있다. 모바일넷의 특징을 살펴보면
jetson nano 보드에서는 하드웨어 자원이 뛰어나지 못하므로 경량화된 모델을 사용해야 한다. 그리고 연산효율도 높아야 하므로 모바일 넷을 주로 사용하고 있는것으로 보인다.
■ Train Augmentation check
train_ssd.py에서 훈련중 사용하는 transform 이다. 9가지 종류의 transform을 사용하고 있다.
■ 훈련중 오류났던 부분
훈련코드에 맞게 데이터를 맞춰주고 훈련을 돌렸다. 데이터를 원하는 형식으로 맞춰주고 path도 바꿔주어서 안될게 없겠다 생각하고 훈련을 진행했는데 오류가 계속 났다.
데이터셋을 불러오는것 까지는 순탄하게 되었지만 train을 하는 부분에서 오류가 자꾸났다. 특히 data loader를 하나씩 끄집어 내는 과정에서 자꾸 오류가 났다. 뭔가 원본 이미지와 레이블데이터가 맞지 않았을 때의 오류인것 같아서 데이터를 파싱하는 부분을 확인해봤다.
파싱하는 부분을 살펴 봤을때 xml형식으로 되어있고 나의 데이터도 xml로 되어있다. (문제없음)
데이터를 꺼내는 부분을 보았는데 name으로 되어있고 나의 데이터에도 name이있음.(문제없음)
bndbox(바운딩박스정보)로 되어있고 나의 데이터에도 bndbox가 있음.(문제없음)
데이터에는 아무문제가 없는데 왜안될까 생각했는데 클레스가 생각이 났다. 클레스 정보가 나는 커스텀 데이터라서 small robot big robot으로 되어있어서 기존 pascal voc 데이터셋과 다르다. 여기서 문제가 생겼을것이라고 판단하고 가보니
역시 기존에 있던 텍스트 파일을 가리키고 있었다. 제대로 된 클래스가 들어가지 않아서 생겼던 문제인것이다.
그래서 내가 정의한 텍스트 파일의 경로로 바꿔서 입력해주고 훈련을 시작했다.
■ 기존 train_ssd.py에서 사용한 하이퍼 파라미터를 가지고 훈련(epoch=30)
30 epoch을 돌리는데 25분 정도 걸린것으로 판단됨
loss가 잘떨어지지 않는다. 이번에도 learning rate의 문제가 있을것으로 판단됨
기본적으로 훈련할때 0.001에서 0.0001의 값을 갖는다고 한다.
tensorboard --logdir=./logs
■ learning rate=0.001로 바꿔서 훈련(epoch=30)
weight decay값을 보니까 5e-4 였다. 이전에 pytorch 예제로 돌렸을때 weight decay는 1e-4였다. 이값이 작으면 작을수록 손실함수 값이 커지게 된다. 너무 작아서 손실함수값이 커지는것으로 판단된다.
■ learning rate=0.001, weight decay=1e-4 로 바꿔서 훈련(epoch=30)
배치사이즈를 더욱더 키워봐야 할것같다
■ learning rate=0.001, batch=8 로 바꿔서 훈련(epoch=100)
epoch 100까지 돌려보았지만 값이 0.8159까지밖에 안떨어짐
■ 로봇 시점에서 찍은 사진으로 모델 테스트 pc에서 실행 (jetson-inference python)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | from vision.ssd.vgg_ssd import create_vgg_ssd, create_vgg_ssd_predictor from vision.ssd.mobilenetv1_ssd import create_mobilenetv1_ssd, create_mobilenetv1_ssd_predictor from vision.ssd.mobilenetv1_ssd_lite import create_mobilenetv1_ssd_lite, create_mobilenetv1_ssd_lite_predictor from vision.ssd.squeezenet_ssd_lite import create_squeezenet_ssd_lite, create_squeezenet_ssd_lite_predictor from vision.ssd.mobilenet_v2_ssd_lite import create_mobilenetv2_ssd_lite, create_mobilenetv2_ssd_lite_predictor from vision.utils.misc import Timer import cv2 import sys if len(sys.argv) < 5: print('Usage: python run_ssd_example.py <net type> <model path> <label path> <image path>') sys.exit(0) net_type = sys.argv[1] model_path = sys.argv[2] label_path = sys.argv[3] image_path = sys.argv[4] class_names = [name.strip() for name in open(label_path).readlines()] if net_type == 'vgg16-ssd': net = create_vgg_ssd(len(class_names), is_test=True) elif net_type == 'mb1-ssd': net = create_mobilenetv1_ssd(len(class_names), is_test=True) elif net_type == 'mb1-ssd-lite': net = create_mobilenetv1_ssd_lite(len(class_names), is_test=True) elif net_type == 'mb2-ssd-lite': net = create_mobilenetv2_ssd_lite(len(class_names), is_test=True) elif net_type == 'sq-ssd-lite': net = create_squeezenet_ssd_lite(len(class_names), is_test=True) else: print("The net type is wrong. It should be one of vgg16-ssd, mb1-ssd and mb1-ssd-lite.") sys.exit(1) net.load(model_path) if net_type == 'vgg16-ssd': predictor = create_vgg_ssd_predictor(net, candidate_size=200) elif net_type == 'mb1-ssd': predictor = create_mobilenetv1_ssd_predictor(net, candidate_size=200) elif net_type == 'mb1-ssd-lite': predictor = create_mobilenetv1_ssd_lite_predictor(net, candidate_size=200) elif net_type == 'mb2-ssd-lite': predictor = create_mobilenetv2_ssd_lite_predictor(net, candidate_size=200) elif net_type == 'sq-ssd-lite': predictor = create_squeezenet_ssd_lite_predictor(net, candidate_size=200) else: predictor = create_vgg_ssd_predictor(net, candidate_size=200) orig_image = cv2.imread(image_path) image = cv2.cvtColor(orig_image, cv2.COLOR_BGR2RGB) boxes, labels, probs = predictor.predict(image, 10, 0.4) for i in range(boxes.size(0)): box = boxes[i, :] cv2.rectangle(orig_image, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (255, 255, 0), 4) #label = f"""{voc_dataset.class_names[labels[i]]}: {probs[i]:.2f}""" label = f"{class_names[labels[i]]}: {probs[i]:.2f}" cv2.putText(orig_image, label, (int(box[0]) + 20, int(box[1]) + 40), cv2.FONT_HERSHEY_SIMPLEX, 1, # font scale (255, 0, 255), 2) # line type path = "run_ssd_example_output.jpg" cv2.imwrite(path, orig_image) cv2.imshow("img",orig_image) cv2.waitKey(0) print(f"Found {len(probs)} objects. The output image is {path}") | cs |
https://github.com/dusty-nv/jetson-inference/blob/master/c/detectNet.cpp
https://github.com/dusty-nv/jetson-inference/blob/master/examples/detectnet/detectnet.cpp
■ jetson-inference build&install on jetson
~$ sudo apt-get update
~$ sudo apt-get install git cmake libpython3-dev python3-numpy
~$ git clone --recursive --depth=1 https://github.com/dusty-nv/jetson-inference
~$ cd jetson-inference
~$ mkdir build
~$ cd build
~$ cmake ../
~$ make -j$(nproc)
~$ sudo make install
~$ sudo ldconfig
■ jetson-inference 기본예제 실행 해보기(jetson-inference C++)
#실행파일 위치 찾기
실행 파일이 만들어지는 곳이 bin폴더 아래에 만들어지므로 bin파일 경로에가서 실행해야한다
# C++
$ ./detectnet --network=ssd-mobilenet-v2 images/humans_4.jpg images/test/test_person2.jpg
■ jetson-inference에서 Custom Model을 사용하기 위한 확장자 변환(.pth -->.onnx)
# .onnx 확장자
.ONNX 파일은 다양한 프레임워크에서 모델을 서로 공유할때 유용하게 사용된다. 이식성이 장점
나는 Custom model을 .pth 확장자에서 .onnx 확장자로 변환해서 쓸것이다.
#.pth-->.onnx 확장자로 변환하는 툴
■ jetson-inference 예제에서 모델을 커스텀 모델로 실행(로봇 데이터셋으로 test)
https://forums.developer.nvidia.com/t/custom-model-initialization-help-jetson-inference-c/182059
# 기본예제의 소스코드
기본 예제의 소스코드에서는 모델을 불러오는 코드가 default로 정해진 모델을 불러와서 사용하고 있다. 나의 커스텀 모델로 예제를 실행하려면 불러오는쪽의 코드를 바꿔줘야한다
# 기본예제의 소스코드중 모델을 불러오는 함수
# Custom model(.onnx)를 load 하기 위한 방법
■ TensorRT 함수 파라미터 분석
Create 함수는 이렇게 선언되어 있는데 그중에 precisionType precision변수에 전달된 값이 TensorRT용 옵션으로 보인다.
precisionType precision은 찾아보니까 열거형으로 나열되어있었고 그중에 옵션을 선택하는 형식으로 되어있다.
이중에 TYPE_FASTEST라는 변수가 디폴트 값으로 설정되어있다.
이 변수는 INT8연산을 우선적으로 진행하고, 안되면 FLOAT16연산, 마지막으로 안되면 FLOAT32연산으로 진행하는 것으로 보인다. (최대한 빠른 연산을 우선적으로 진행하는것 같음)
■ jetson-inference 영상으로 테스트
댓글
댓글 리스트-
작성자정다훈 작성자 본인 여부 작성자 작성시간 24.03.12 https://github.com/dusty-nv/jetson-inference
-
작성자정다훈 작성자 본인 여부 작성자 작성시간 24.03.15 https://github.com/qfgaohao/pytorch-ssd/tree/master
-
작성자Sungryul Lee 작성시간 24.03.20 출력결과에 TRT -> TensorRT를 의미 그러니까 tensorrt model로 변환되어 추론함을 의미
현재 어떤 옵션으로 경량화되었는지 조사하고 옵션별로 실행시간을 측정하여 비교해볼 필요 있음
현 모델은 1장 당 1.5sec(1504.9758msec) 정도 걸리는것 같은데 더 줄여야함 -
작성자Sungryul Lee 작성시간 24.03.22 jetson 라이브러리말고 파이토치라이브러리로 훈련한 모델을 젯슨보드에서 테스트해볼것
훈련은 PC에서 파이토치라이브러리로 하는게 관리가 쉬울것 같음
