Closed jungae-park closed 2 years ago
오케이. 이건 혹시 tpu 에서 혹은 tpu 에서 제공해주는 모델에서는 가변 배치 사이즈를 지원하지 않을수 있겠다는 생각이 문득 드네. 조금 더 같이 찾아보자.
네 해당 에러를 조금 더 살펴보겠습니다.
모든 모델에서 bach size (2,4,8,16,32,64,128) 를 늘려서 추론하는 것은 여전히 위와 같은 에러가 발생 하여 다른 자료와 함께 살펴보고 있습니다.
https://stackoverflow.com/questions/63877776/speed-up-multiple-model-inference-on-edge-tpu
2년전 포스트이기는 한데 coral tpu 는 배칭을 지원하지 않는것 같네. 일단 그 당시에는.
네 교수님. tpu 환경에서 batch size에 따른 실험을 진행한 논문이 있는지도 찾아보겠습니다.
coral tpu에서 batch size를 support 해주는지에 대한 논문을 찾아보았습니다. 관련된 논문이 많이 있지는 않았지만 아래 논문을 참고해볼만하여 공유드립니다.
SensiX++: Bringing MLOPs and Multi-tenant Model Serving to Sensory Edge Devices (6.2.1 절의 내용) https://arxiv.org/pdf/2109.03947.pdf
Coral TPU framework has different characteristics from other frameworks.
First, it does not allow the concurrent access to Coral TPUs from different
processors.
Thus, we develop a unified container that manages all the inferences
of TPU models and adopt a round-robin scheduler for fairness.
Second, it does not support batch processing and corresponding
internal optimisation in the framework due to the limited memory (8 MB).
However, when there are multiple models, the execution of a batch of
samples at once in TPU framework also enables high throughput.
좀 더 자료를 찾아보겠습니다.
"이전에 참고하였던 튜토리얼에서 미니배치 단위로 학습이 진행되는 것을 확인" 이건 어디서 확인을 한거야? 코랄 tpu 에서 학습을 진행한다는게 잘 이해가 되지는 않는것 같아. 그리고 yolo 에서 배치 단위로 실행하는 코드는 어떻게 동작하는거야? 유의미한 성능 차이가 있는것 같아? 내 판단에는 추론에서 배칭은 지원해주지 않는것 같은 느낌이 들어서..
그리고 이전에 참고하였던 튜토리얼은 코랩에서 작성된 CPU로 batch size에 따라 trining된 것이였고, 추론과는 상관이 없는 것을 확인하였습니다. 또한 yolo v5(image/video)에서는 batch size에 따라 추론을 진행하였는데, 결과 값이 IPS를 기준으로 살펴보았을 때, image의 경우 값이 0.3정도로 비슷하여 의미 있지는 않았고 , video의 경우 값이 의미가 있어보였습니다. 그래서 yolo에서 video detection의 경우 batch size에 따라 어떤 방법으로 추론하고 있었는지 다시 코드를 살펴보고 있습니다.
batch size에 따른 실험 결과가 포함된 논문도 없고, 자료도 없어 애매하지만 TPU가 batch size를 support 하지 않을 수도 있을 것 같습니다.
그러면 yolo video detection 이 어떻게 하는지만 확실하게 확인하고 넘어가면 되겠다. 해당 내용을 기록해주면 좋을것 같아.
batch size가 1 일 때, 모든 모델에서 추론은 완료하였고, batch size가 늘어남에 따라 추론이 가능한지 yolo v5 video detection이 어떻게 batch size에 따라 진행되었는지 확인해보겠습니다.
오케이. 정애야 yolo 에서 배치 사이즈가 달라졌을때 실제로 어떻게 진행이 되었는지 확인을 해보기는 해야하는구나. 마저 진행해서 마무리를 지어보자.
yolov5 모델에서 video detection을 배칭하여 추론하였을 때 값이 의미가 있어보여 확인을 먼저 해보았습니다.
21년도 기준 다른 논문을 찾아봐도 coral tpu가 배칭을 지원한다는 내용이 정확히 없고, 애매합니다. 그래서 아래와 같이 지원 가능/불가능으로 기준을 구분하여 실험을 추가로 진행해 봐야할지 고민하고 있습니다. 시간이 많이 소요되지 않을까 우려가 되기는 합니다.
[지원 가능]
[지원 불가능]
응 정애야. 내생각에는 우선 코드를 보고 정말로 배치로 되고 있는지를 봐볼 필요가 있기는 하겠다. 코드의 어떤 부분인지를 공유를 해줄래? 관련한 실험 결과가 있으면 같이 보면 좋을것 같아.
네 교수님 yolov5 video detection 코드는 아래 링크에서 59-98라인입니다. https://github.com/ddps-lab/edge-inference/blob/1af5d69aa96c46b7ac1ff97b44699c296d5f18b1/CNN/video_detection.py
# Run inference
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup
dt, seen = [0.0, 0.0, 0.0], 0
iftime = []
iftime_start = time.time()
for path, im, im0s, vid_cap, s in dataset:
im = torch.from_numpy(im).to(device)
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
im /= 255 # 0 - 255 to 0.0 - 1.0
if len(im.shape) == 3:
im = im[None] # expand for batch dim
# Inference
iftime_avg_start = time.time()
pred = model(im, augment=augment, visualize=False)
# NMS
pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
# Process predictions
for i, det in enumerate(pred): # per image
seen += 1
p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0)
print(frame)
if len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
c = int(cls) # integer class
label = f'{names[c]} {conf:.2f}'
print(label)
iftime_avg_end = time.time() - iftime_avg_start
iftime.append(iftime_avg_end)
iftime = np.array(iftime)
inference_time = time.time() - iftime_start
pytorch 모델을 사용하지 않았기 때문에 bs 변수를 활용해서 batch size에 따라 추론을 하고 있습니다. 그리고, 참고한 코드 github에서도 batch 단위별로 video detction을 할 수있도록 구현하였다라는 내용을 아래 링크에서 확인하였습니다. https://github.com/ultralytics/yolov5/issues/604
yolov5 image detection의 경우도 아래 코드의 111-175라인 처럼 batch size에 따라 추론을 진행 할 수 있도록 코드가 구현된 상태입니다. https://github.com/ddps-lab/edge-inference/blob/1af5d69aa96c46b7ac1ff97b44699c296d5f18b1/CNN/od_inference.py
하지만 coral tpu 환경에서 image/video detection을 진행했을 때의 IPS 결과값이 image의 경우는 의미가 없어 보이고 video의 경우에는 의미가 있어 보여 왜 이런 차이가 나는지 알 수가 없습니다. https://docs.google.com/spreadsheets/d/1hhYyZWexZVVmE8NIR5lIlMwPGjTQkHUEFYAR46YJ9tw/edit#gid=1792697168
coral tpu가 yolov5 image 배칭 추론을 못하는 것이라면 cnn 배칭 추론 에러가 발생한 것이 이해가 될 수도 있을 것 같습니다. 그래서 yolov5 video 배칭 추론을 프레임수가 더 많고 긴 동영상을 데이터셋으로 하여 다시 살펴보려고 합니다. (현재는 20초 가량의 38프레임 동영상이라 배칭 추론이 되는 것처럼 보이는 것일 수도 있을 것이라고 생각 됩니다.)
링크해준 코드를 보면 bs=1 은 상수로 설정되어 있는데, 해당 값이 어떻게 변해서 배치 사이즈가 변하는거야?
bs = 1 # batch_size
vid_path, vid_writer = [None] * bs, [None] * bs
dataset_load_time = time.time() - dataset_load_time
# Run inference
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup
이 부분에서 뭔가를 변화 시켜줄것 같은데. 코드 신택스가 잘 이해가 안되네.
동영상의 길이가 굳이 문제가 될것 같지는 않은데.. tpu 가 아닌 다른 환경에서의 성능과도 함께 비교를 해보자.
그리고 코딩할때 변수명을 조금 더 신경써서 지어주자. 변수명 쓸때 불필요하게 짧게 하는게 너무 많아. 예를 들어 for path, im, im0s, vid_cap, s in dataset: im, im0s, vid_cap 가 무슨 변수인지 알기가 어려워.
코딩에서 변수명 줄여 쓴다고 좋은 코드 아니니까 최대한 readable 한 변수명을 사용하도록 하자.
일단은 지금 코드로는 batch size 가 변한다고 생각되지는 않으니 한번 확인해줘.
네 교수님 코드 분석을 다시 해보겠습니다. 그리고 다른 환경에서의 성능과도 다시 비교해보겠습니다. 코드를 참고한 github 것을 가져오다보니 변수명이 짧게 쓰여진게 많은 것 같습니다. 다시 보면서 수정해보겠습니다.
우선 지금까지 yolo video 에서 배치 크기 변경 실험은 어떻게 진행했는지 공유를 해줄래?
python3 video_detection.py --weights ./yolov5s-fp16_edgetpu.tflite --source ./dataset/video/road.mp4 --data ./model/yolo_v5/coco.yaml
이렇게 진행하여 시트에 결과 값을 정리하였습니다.
video_detection.py 의 bs 변수값을 실험 할때마가 매번 코드를 수정해서 진행했다는 이야기야?
네 맞습니다
아이고. 내가 실험 코드도 봐줬어야 했나보다. 여럿이 실험을 하면 서로 코드를 봐주고 좋은건 서로 공유하고 하면 좋을텐데.. 이렇게 매번 코드를 수정하면 실험 결과의 관리나 진행이 쉽지않았을것 같은데. 일단 지금 코드상에서 bs 가 바뀌면 어떻게 동작하는지 분석해봐야겠다.
네 지금 코드상에서 bs 역할을 다시 살펴보겠습니다.
yolov5 image detection, yolov5 video detection 코드를 다시 살펴보았고, 아래 노션으로 정리해보았습니다. https://encouraging-lantern-5d1.notion.site/bs-d5aac66a23b74d0aa54df5b7268254d0
TF 모델 기반 배칭 추론 코드는 잘 작성되었지만, edgetpu 모델에 대한 배칭 추론 코드는 작성되어 있지 않은 것 같기도 하여 좀 더 살펴보고 있습니다. (warmup_type에 saved_model=TF) gpu에서만 배칭 추론이 가능해 보입니다.
그리고 yolov5 모델 배칭 추론 관련하여 아래와 같은 자료를 찾았습니다.
[결과 정리]
warmup code 의 분석에도 논리적으로 부족한 부분이 있는것 같은데... 일단 그것보다 지금 보니 yolo v5 image 실험에서는 tpu 제외 다른 장비에서도 배치 효과가 없고, 코드가 정상적인지 확실치 않은것 같네. 해당 코드가 예상대로 잘 동작하는지 확인을 아직 못했구나. YOLO V5 코드의 배치 사이즈를 변화하는 코드를 어떻게 동작시키는지 확인을 해본게 있어? 이전 코멘트에서 이야기했듯이 코드에서 bs 를 실험할때마다 매번 수정해야 한다는것 자체가 조금 이상했었어.
YOLOV5 가 배칭을 지원하지 않는게 아니라 코드를 사용하는 법을 잘 모르고 그냥 계속 사용했던 문제인것 같아.
위쪽의 분석내용 관련해서는
아래와 같은 단계로 논리적인 코드 분석을 다시 해보겠습니다.
이미지 분류 모델 배칭 추론 코드 작성은 현재 batch size 1로만 진행했으므로 2,4,8,16,32,64,128 batch size로도 실험을 진행해 볼 필요가 있다는 의미였습니다.
"이미지 분류 모델 배칭 추론 코드 작성은 현재 batch size 1로만 진행했으므로" 이라고 하는데 이미지 분류 모델은 어떤거야? 지금 실험 결과에서 배치 사이즈가 1만 진행된건 없는것 같아서.
jetson 환경에서는 mobilenet v1, v2,inception v3 모델에 대해 batch size 모두 실험을 진행하였지만, tpu 환경에서 mobilenet v1, v2,inception v3 모델은 batch size 1로 진행하였습니다.
tpu 환경에서는 mobilent 이나 inception 은 배치 사이즈 크게 했을때 에러가 나지 않았어? 아니면 아예 시도조차 안했던 거였나? 안되었던걸로 기억이 나는데 어떤 이슈였는지 정확히 기억이 나지는 않네.
그때 이슈는 tfrecord 데이터셋을 사용하였을때 배치 추론 에러가 발생하였고, 정확도 문제가 있어서 원본 이미지로 변경하여 실험을 진행하였습니다. 원본 이미지 추론은 batch 1으로만 진행되었고, 나머지 배치 추론은 yolo 실험을 확인 먼저 해보는 방향으로 진행하기로 하였습니다.
원본이미지 사용과 tfrecord 사용의 이슈였구나. 결론으로 원본이미지를 사용했을때 정확도는 유지 되면서 처리속도는 tfrecord 와 별 차이가 없어서 괜찮아서 원본이미지로 진행을 하자고 했던것 같네. 그렇다면 궁금한점은 현재 코랄의 홈페이지에서 제공해주는 모델에서 배치 사이즈를 증가 시킬수 있는 옵션이나 변수가 있어? 실험을 진행하기전에 코드 부터 확인해보면 될것같아.
코랄 공홈에서 제공하는 모델에 대한 기본 코드에는 배치 사이즈를 증가 시킬수 있는 옵션이나 변수는 없습니다. 배칭 추론을 한다면 따로 만들어서 사용 해야 할 것 같습니다.
코드를 완전히 새롭게 해야 할것 같은데 꽤나 믾으누시간과 애너지가 필요할것 같아. 두현한다해도 된다는 보장도 없고. 시간은 한정되어 있으니 더 중요한 일에 시간을 쓰는게 나을것 같아서.
네 yolov5 배칭 추론 코드 분석을 더 진행하겠습니다.
먼저 video detection 추론 코드부터 분석해보았습니다. (image detection 추론 코드도 비슷합니다.) https://encouraging-lantern-5d1.notion.site/bs-d5aac66a23b74d0aa54df5b7268254d0
[video detection]
# video detection
## video_detection.py 파일의 47라인에서 모델 로드 (DetectMultiBackend 함수 사용)
model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
## common.py 파일의 308라인에서 DetectMultiBackend 함수를 실행시키며,
## 이때 TensorFlow SavedModel, edgetpu 모델을 불러오기 위해 사용되는 device는 cpu사용
def __init__(self, weights='yolov5s.pt', device=torch.device('cpu'), dnn=False, data=None, fp16=False):
YOLOv5 \U0001f680 2022-5-19 torch 1.10.2 CPU (추론 실행시 출력되는 내용)
## common.py 파일의 279, 369, 387라인에서 DetectMultiBackend 함수를 실행시키며,
## TensorFlow SavedModel, edgetpu 모델을 불러와 로드시킴
## 그리고 edgetpu의 경우 바로 tpu device 라이브러리를 할당하여 추론에 사용하도록 환경 설정
## TensroFlow SavedModel의 경우 추론시 사용하는 device 설정은 아래에서 진행
else: # TensorFlow (SavedModel, GraphDef, Lite, Edge TPU)
if saved_model: # SavedModel
LOGGER.info(f'Loading {w} for TensorFlow SavedModel inference...')
import tensorflow as tf
keras = False # assume TF1 saved_model
model = tf.keras.models.load_model(w) if keras else tf.saved_model.load(w)
if edgetpu: # Edge TPU https://coral.ai/software/#edgetpu-runtime
LOGGER.info(f'Loading {w} for TensorFlow Lite Edge TPU inference...')
delegate = {
'Linux': 'libedgetpu.so.1',
'Darwin': 'libedgetpu.1.dylib',
'Windows': 'edgetpu.dll'}[platform.system()]
interpreter = Interpreter(model_path=w, experimental_delegates=[load_delegate(delegate)])
## video_detection.py 파일의 55라인에서 bs 변수에 batch size 지정
bs = 1 # batch_size
## video_detection.py 파일의 60라인에서 model warmup 함수 실행을 위해
## batch size, channel,image size 전달
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup
## common.py 파일의 472라인에서 batch size 1에 대해 추론을 한번 실행하여 모델 warmup
## 장비에서 처음 추론 시간이 길기 때문에 coldstart를 줄이기 위해 사용되는 함수로 확인
## warmup 함수가 각 모델의 device에서 forward 함수를 실행시켜 추론 진행
def warmup(self, imgsz=(1, 3, 640, 640)):
# Warmup model by running inference once
warmup_types = self.pt, self.jit, self.onnx, self.engine, self.saved_model, self.pb
if any(warmup_types) and self.device.type != 'cpu':
im = torch.zeros(*imgsz, dtype=torch.half if self.fp16 else torch.float, device=self.device) # input
for _ in range(2 if self.jit else 1):
self.forward(im) # warmup
## device는 TensroFlow SavedModel의 경우 추론을 위해 torch_utils.py 파일의 52, 59라인에서
## select_device 함수를 실행시켜 cuda device 사용
device = select_device(device)
def select_device(device='', batch_size=0, newline=True):
# device = 'cpu' or '0' or '0,1,2,3'
s = f'YOLOv5 🚀 {git_describe() or file_update_date()} torch {torch.__version__} ' # string
device = str(device).strip().lower().replace('cuda:', '') # to string, 'cuda:0' to '0'
cpu = device == 'cpu'
if cpu:
os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # force torch.cuda.is_available() = False
elif device: # non-cpu device requested
os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable - must be before assert is_available()
assert torch.cuda.is_available() and torch.cuda.device_count() >= len(device.replace(',', '')), \
f"Invalid CUDA '--device {device}' requested, use '--device cpu' or pass valid CUDA device(s)"
cuda = not cpu and torch.cuda.is_available()
if cuda:
devices = device.split(',') if device else '0' # range(torch.cuda.device_count()) # i.e. 0,1,6,7
n = len(devices) # device count
if n > 1 and batch_size > 0: # check batch_size is divisible by device_count
assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}'
space = ' ' * (len(s) + 1)
for i, d in enumerate(devices):
p = torch.cuda.get_device_properties(i)
s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / (1 << 20):.0f}MiB)\n" # bytes to MB
else:
s += 'CPU\n'
if not newline:
s = s.rstrip()
LOGGER.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s) # emoji-safe
return torch.device('cuda:0' if cuda else 'cpu')
## common.py 파일의 410, 448, 454라인에서 forward 함수를 실행하여
## 입력 데이터의 형식을 모델에 맞게 맞추고 추론 실행
if self.saved_model: # SavedModel
y = (self.model(im, training=False) if self.keras else self.model(im)).numpy()
else: # Lite or Edge TPU
input, output = self.input_details[0], self.output_details[0]
int8 = input['dtype'] == np.uint8 # is TFLite quantized uint8 model
if int8:
scale, zero_point = input['quantization']
im = (im / scale + zero_point).astype(np.uint8) # de-scale
self.interpreter.set_tensor(input['index'], im)
self.interpreter.invoke() # TPU에서 추론
y = self.interpreter.get_tensor(output['index'])
if int8:
scale, zero_point = output['quantization']
y = (y.astype(np.float32) - zero_point) * scale # re-scale
y[..., :4] *= [w, h, w, h] # xywh normalized to pixels
## video_detection.py 파일의 45, 64라인에서 batch size 1로 한번 추론한 모델을 사용하여
## dataset 만큼 추론 진행하며 입력으로 dataset image shape, box shape, video를
## 입력으로으로 받아 추론 시작
device = select_device(device)
for path, im, im0s, vid_cap, s in dataset:
im = torch.from_numpy(im).to(device)
## TensroFlow SavedModel의 경우 추론을 위해 torch_utils.py 파일의 52, 59라인에서
## select_device 함수를 실행시켜 cuda device 사용
def select_device(device='', batch_size=0, newline=True):
# device = 'cpu' or '0' or '0,1,2,3'
s = f'YOLOv5 🚀 {git_describe() or file_update_date()} torch {torch.__version__} ' # string
device = str(device).strip().lower().replace('cuda:', '') # to string, 'cuda:0' to '0'
cpu = device == 'cpu'
if cpu:
os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # force torch.cuda.is_available() = False
elif device: # non-cpu device requested
os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable - must be before assert is_available()
assert torch.cuda.is_available() and torch.cuda.device_count() >= len(device.replace(',', '')), \
f"Invalid CUDA '--device {device}' requested, use '--device cpu' or pass valid CUDA device(s)"
cuda = not cpu and torch.cuda.is_available()
if cuda:
devices = device.split(',') if device else '0' # range(torch.cuda.device_count()) # i.e. 0,1,6,7
n = len(devices) # device count
if n > 1 and batch_size > 0: # check batch_size is divisible by device_count
assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}'
space = ' ' * (len(s) + 1)
for i, d in enumerate(devices):
p = torch.cuda.get_device_properties(i)
s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / (1 << 20):.0f}MiB)\n" # bytes to MB
else:
s += 'CPU\n'
if not newline:
s = s.rstrip()
LOGGER.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s) # emoji-safe
return torch.device('cuda:0' if cuda else 'cpu')
## video_detection.py 파일의 73라인부터 video frame 하나당 추론 진행
## 위에서 batch size 1로 한 번 추론된 모델에 데이터셋을 입력으로 넣고 추론 후
## bounding box 처리 및 결과 계산
pred = model(im, augment=augment, visualize=False)
따라서 image, video detection의 경우 batch size에 따른 추론을 진행한 것이 맞다고 생각이 듭니다. 다만, image detection의 경우 batch size에 따라 값이 모두 일정한것이 이상하여 추가로 image detection 코드에서 batch size 옵션 부분을 다시 살펴보고 있습니다.
위 코드의 마지막 부분에 있는 model(im, augment, ~~) 여기서 실제로 추론이 일어나는것 같은데 데이터 셋은 어떤게 입력이 되는거야?
그리고, model.warmup(imgsz=(1 if pt else bs, 3, *imgsz))
위의 명령어에서 pt 변수의 역할은 어떤거야?
video detection 추론에서 데이터셋이 어떤 게 입력되고, pt 변수의 역할이 무엇인지 아래와 같이 정리해보았습니다.
## 추론에 들어가는 데이터셋은 video_detection.py 파일의 40-42라인에서
## source에 비디오 데이터셋을 입력으로 받음
source = str(source)
is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
source = check_file(source) # download
## 그리고 video_detection.py 파일의 54라인에서 LoadImages 함수를 실행하여 frame을 읽어옴
dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt)
## datasets.py 파일의 178, 186, 219라인에서 LoadIamges 클래스를 실행하여
## 입력 받은 비디오 파일을 frame으로 읽어옴
class LoadImages:
# YOLOv5 image/video dataloader, i.e. `python detect.py --source image.jpg/vid.mp4`
def __init__(self, path, img_size=640, stride=32, auto=True):
elif os.path.isfile(p):
files = [p] # files
...
if self.video_flag[self.count]:
# Read video
self.mode = 'video'
ret_val, img0 = self.cap.read()
while not ret_val:
self.count += 1
self.cap.release()
if self.count == self.nf: # last video
raise StopIteration
else:
path = self.files[self.count]
self.new_video(path)
ret_val, img0 = self.cap.read()
self.frame += 1
s = f'video {self.count + 1}/{self.nf} ({self.frame}/{self.frames}) {path}: '
## 그렇게 받아온 dataset을 기준으로 video_detection.py 파일의 45, 60, 64, 73라인에서 추론 시작
## pt는 torch 기반 모델이므로, TensorFlow SavedModel과 edgetpu 모델의 경우
## batch size에 따라 warmup 모델을 사용하여 추론
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup
for path, im, im0s, vid_cap, s in dataset:
im = torch.from_numpy(im).to(device)
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
im /= 255 # 0 - 255 to 0.0 - 1.0
if len(im.shape) == 3:
im = im[None] # expand for batch dim
# Inference
iftime_avg_start = time.time()
pred = model(im, augment=augment, visualize=False)
warmup 에서는 batch size 가 pt 가 false 일 경우에 설정된다고 하더라도, 본 추론에서는 어떻게 배칙 사이즈가 설정되는거야? 실제 성능 측정을 warmup 을 하는거야?
제가 생각하기로는 batch size를 이미 warmup에서 지정하여 모델 추론을 한 번 하였기 때문에 또 batch size를 지정하지 않고 warmup을 진행한 모델을 가져와 데이터셋에 대해 추론을 진행하는 것 같습니다. 실제 성능 측정도 warmup 후에 측정하고 있습니다. 코드를 조금 더 살펴보겠습니다.
video detection 은 어떨지 모르지만 일반적으로 모델에 입력을 줄때 배치 사이즈별로 넣어주거든. warm up 모델에서 했듯이. 그런데 지금 코드에서 실제로 "pred = model(im, augment=augment, visualize=False)" 실행할때 batch size 를 지정하지 않는게 이상한거야. 정애가 생각한대로 될수는 있는데 그건 어디까지나 추측이니.. 확실한 근거를 봤으면 해. 운호랑 같이 분석해서 결론을 내보자.
지금 성능 측정은 pred = model(im, augment=augment, visualize=False) 이부분에서 하고 있어?
image detection 코드 같은 경우는 말씀하신 것처럼 모델에 입력을 줄 때 배치 사이즈 별로 주는 코드 이기는 합니다.
그런데 이건 배치 사이즈 별로 추론을 진행하는 것이 아닌, 총 이미지 개수 중 추론이 처리 되는 이미지 개수를 bar (1/5000) 형식으로 보여주는 것 같아 실제 배칭 추론은 아닌 것 같습니다. https://github.com/ddps-lab/edge-inference/blob/main/CNN/od_inference.py
# model inference
iftime = []
iftime_start = time.time()
for batch_i, (im, targets, paths, shapes) in enumerate(pbar):
t1 = time_sync()
if cuda:
im = im.to(device, non_blocking=True)
targets = targets.to(device)
im = im.half() if half else im.float() # uint8 to fp16/32
im /= 255 # 0 - 255 to 0.0 - 1.0
nb, _, height, width = im.shape # batch size, channels, height, width
t2 = time_sync()
dt[0] += t2 - t1
# image inference
iftime_avg_start = time.time()
out, train_out = model(im, augment=augment, val=True) # inference, loss outputs
dt[1] += time_sync() - t2
성능 측정은 아래와 같은 형태로 warmup 후 데이터 셋을 받아와 pred 부분에서 측정을 하고 있습니다.
# Run inference
model.warmup(imgsz=(1 if pt else bs, 3, *imgsz)) # warmup
dt, seen = [0.0, 0.0, 0.0], 0
iftime = []
iftime_start = time.time()
for path, im, im0s, vid_cap, s in dataset:
im = torch.from_numpy(im).to(device)
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
im /= 255 # 0 - 255 to 0.0 - 1.0
if len(im.shape) == 3:
im = im[None] # expand for batch dim
# Inference
iftime_avg_start = time.time()
pred = model(im, augment=augment, visualize=False)
# NMS
pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
# Process predictions
for i, det in enumerate(pred): # per image
seen += 1
p, im0, frame = path, im0s.copy(), getattr(dataset, 'frame', 0)
print(frame)
if len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(im.shape[2:], det[:, :4], im0.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
c = int(cls) # integer class
label = f'{names[c]} {conf:.2f}'
print(label)
iftime_avg_end = time.time() - iftime_avg_start
iftime.append(iftime_avg_end)
iftime = np.array(iftime)
inference_time = time.time() - iftime_start
image, video detection 모두 배칭 추론 근거가 될 만한 부분을 다시 분석해보겠습니다.
그래서 yolov5 image/video detection 배칭 추론 코드 다시 작성 및 실험 필요하고, 시간이 다소 소요 될 것 같습니다. (nvidia jetson, tpu)
오케이. 운호도 같이해서 다시 진행을 하자.
참고하는 yolov5 github의 image detection 코드를 확인 해보던 중에 torch model이 아닌 경우, 다른 모든 모델 추론시 batch size를 1로 고정한다는 내용을 발견하였습니다. https://github.com/ultralytics/yolov5/blob/master/val.py
# Load model
model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine
imgsz = check_img_size(imgsz, s=stride) # check image size
half = model.fp16 # FP16 supported on limited backends with CUDA
if engine:
batch_size = model.batch_size
else:
device = model.device
if not (pt or jit):
batch_size = 1 # export.py models default to batch-size 1
LOGGER.info(f'Forcing --batch-size 1 square inference (1,3,{imgsz},{imgsz}) for non-PyTorch models')
그래서 추가로 다시 확인을 해보니 pytorch 기반 모델을 TF 모델로 export 할 때, 기본인 batch size를 1이 아닌 2로 변경하여 모델을 다시 추출할 수 있었습니다. (입력 형식 : batch size: channel : image size → 2,3,640,640) https://github.com/ultralytics/yolov5/blob/master/export.py
python3 export.py --weights yolov5s.pt --include saved_model --batch-size 2
batch size가 2인 TF 모델을 다시 기존 코드에서 batch size를 2로 지정하고 추론하였더니 정상정으로 배치 추론이 가능해보입니다. (jetson nano 환경) 코드는 입력 형식을 (2,3,640,640)을 맞추어 이미지를 생성하고 warmup으로 한번 perdict 하고 저장 후, input 정보가 적용된 모델로 추론을 진행하는 형태인 것 같습니다.
https://github.com/ultralytics/yolov5/blob/master/val.py
python3 val.py --weights ./yolov5s_saved_model \
--data ../model/yolo_v5/coco.yaml --batch_size 2 --img 640 --iou 0.5 \
--half --task val
현재 nano 장비에서는 메모리 이슈로 결과값을 정확하게 보지 못하였지만 다른 장비에서 다시 살펴보고 결과 값은 업데이트 하겠습니다.
따라서 배치 추론을 하기 위해서는 TF 모델로 export 할 때 batch 단위별로 모델을 모두 가지고 있어야 실제 추론코드에서 배치 추론이 가능할 것으로 예상됩니다.
이런 방식으로 모든 장비에서 다시 실험을 진행해보고자 합니다.
nano 장비에서는 메모리 이슈가 있어, xavier 장비에서 위와 같은 방법으로 image detection 배칭 추론을 하였을 때 아래와 같이 결과 값을 얻을 수 있었습니다. (batch size 2)
Speed: 2.8ms pre-process, 60.2ms inference, 12.1ms NMS per image at shape (2, 3, 640, 640)
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|##########| 2500/2500 [07:17<00:00, 5.72it/s]
all 5000 36321 0.668 0.528 0.568 0.369
Speed: 2.8ms pre-process, 60.2ms inference, 12.1ms NMS per image at shape (2, 3, 640, 640)
video detection의 경우도 이런 방법으로 배칭 추론을 할 수 있을지 살펴보겠습니다.
video 쪽은 운호가 해보는게 어떨까?
넵, 확인해보겠습니다.
교수님 xavier 장비에서 yolov5 model을 batch 단위별로 추론을 진행하고 아래과 같이 결과값을 정리하고 있습니다. (batch 추론이 잘 되고 있다는 것을 배치 단위별로 pred 해야하는 개수 확인하고 진행하고 있습니다.)
# batch 1
AP@0.5 = 0.5683492051258545
AP@0.5:0.95 = 0.36936729321612
model_load_time = 5.380649089813232
dataset_load_time = 0.4046156406402588
inference_time = 510.71391129493713
inference_time(avg) = 0.06801025934219361
IPS = 9.680557553792118
IPS(inf) = 14.70366397176197
# batch 2
AP@0.5 = 0.5684785273053917
AP@0.5:0.95 = 0.3694348013240066
model_load_time = 13.784666061401367
dataset_load_time = 0.6699466705322266
inference_time = 442.6402814388275
inference_time(avg) = 0.06337808961868287
IPS = 10.938647671991069
IPS(inf) = 15.778323487131676
# batch 4
AP@0.5 = 0.5688828829951357
AP@0.5:0.95 = 0.36974638232580725
model_load_time = 5.272364377975464
dataset_load_time = 0.518693208694458
inference_time = 383.36520051956177
inference_time(avg) = 0.05554096541404724
IPS = 12.84830937662861
IPS(inf) = 18.004728447645657
그런데, batch 8부터 아래와 같이 shared memory limit 이슈가 생겨 확인을 해보니 컨테이너와 호스트가 공유하는 메모리 공간이 shm인데 그 공간이 적어서 생긴 에러라고 합니다. 그래서 컨테이너 생성시에 --shm-size 옵션으로 메모리에 따라 shm을 넉넉히 5G로 지정하여 컨테이너 생성 후 batch 8 추론을 다시 진행하였습니다. (기본 128MB)
RuntimeError: DataLoader worker (pid 318) is killed by signal:
Bus error. It is possible that dataloader's workers are out of shared memory.
Please try to raise your shared memory limit.
docker run -it --shm-size=5G --privileged edge-inference /bin/bash
그런데 아래와 같이 shm size를 지정하지 않고 batch 4로 추론을 진행했던 것보다 성능이 떨어지는 상황입니다.
# batch 8
AP@0.5 = 0.5689068897791872
AP@0.5:0.95 = 0.36953006393004023
model_load_time = 3.579603910446167
dataset_load_time = 3.0116233825683594
inference_time = 1475.1527543067932
inference_time(avg) = 0.2726538516521454
IPS = 3.374402097858771
IPS(inf) = 3.667654038043117
[실험 내용]
새로운 컨테이너4(shm-16G)를 새롭게 생성 후 추론시 GPU를 사용하지 않고, CPU 사용
gpu를 사용하지 않고 cpu를 사용하였을 때 메시지
root@c49f3e217cbd:/edge-inference/CNN# python3 val.py --weights ./batch4/yolov5s_saved_model \
> --data ./model/yolo_v5/coco.yaml --batch_size 4 --img 640 --iou 0.5 \
> --half --task val
/usr/local/lib/python3.6/dist-packages/torchvision/io/image.py:11: UserWarning: Failed to load image Python extension:
warn(f"Failed to load image Python extension: {e}")
Downloading https://ultralytics.com/assets/Arial.ttf to /root/.config/Ultralytics/Arial.ttf...
2022-06-30 05:28:51.674391: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.10.2'; dlerror: libcudart.so.10.2: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/lib/python3.6/dist-packages/cv2/../../lib64:/usr/local/cuda/lib64:/usr/local/cuda-10.2/targets/aarch64-linux/lib:
2022-06-30 05:28:51.674505: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
2022-06-30 05:28:55.565305: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/lib/python3.6/dist-packages/cv2/../../lib64:/usr/local/cuda/lib64:/usr/local/cuda-10.2/targets/aarch64-linux/lib:
2022-06-30 05:28:55.565382: W tensorflow/stream_executor/cuda/cuda_driver.cc:269] failed call to cuInit: UNKNOWN ERROR (303)
2022-06-30 05:28:55.565481: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (c49f3e217cbd): /proc/driver/nvidia/version does not exist
val: data=./model/yolo_v5/coco.yaml, weights=['./batch4/yolov5s_saved_model'], batch_size=4, imgsz=640, conf_thres=0.001, iou_thres=0.5, task=val, device=, workers=8, single_cls=False, augment=False, verbose=False, save_txt=False, save_hybrid=False, save_conf=False, save_json=True, project=runs/val, name=exp, exist_ok=False, half=True, dnn=False
Python 3.7.0 required by YOLOv5, but Python 3.6.9 is currently installed
YOLOv5 \U0001f680 2022-5-19 torch 1.10.2 CPU
Loading batch4/yolov5s_saved_model for TensorFlow SavedModel inference...
Loading batch4/yolov5s_saved_model for TensorFlow SavedModel inference...
Importing a function (__inference_pruned_6158) with ops with custom gradients. Will likely fail if a gradient is requested.
Importing a function (__inference_pruned_6158) with ops with custom gradients. Will likely fail if a gradient is requested.
val: Scanning '/edge-inference/CNN/model/yolo_v5/datasets/coco/val2017' images and labels...4952 found, 48 missing, 0
val: New cache created: /edge-inference/CNN/model/yolo_v5/datasets/coco/val2017.cache
val: New cache created: /edge-inference/CNN/model/yolo_v5/datasets/coco/val2017.cache
Class Images Labels P R mAP@.5 mAP@.5:.95: 0%| | 0/1250 [00:002022-06-30 05:29:04.860625: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2022-06-30 05:29:05.022911: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 31250000 Hz
Class Images Labels P R mAP@.5 mAP@.5:.95: 2%|1 | 19/1250 [00:2 Class Images Labels P R mAP@.5 mAP@.5:.95: 2%|1 | 19/1250 [00:3
gpu를 사용하고 있을 때 메시지는 컨테이너 삭제로 인해 찾을 수가 없었습니다.
눈으로 확인 하였을 때 제대로 gpu를 사용하였을 경우, 아래와 같은 메시지로 gpu에 할당된 메모리 크기를 출력해야함
2022-02-08 05:38:24.294486: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 66 MB memory) -> physical GPU (device: 0, name: NVIDIA Tegra X1, pci bus id: 0000:00:00.0, compute capability: 5.3)
[정리]
https://github.com/ddps-lab/research-issues/issues/45
현재 batch size 1에서는 추론이 가능하지만, batch size 2부터는 아래와 같은 에러가 발생하여 확인하고 있습니다.