[Object Detection] COCOEval에서 -1.000이 뜰 때 해결법

Problem: Precision이나 Recall이 -1.000 (잘못된 값)으로 나타남

Precision이나 Recall이 -1일 때는 areaRng의 small, medium, large가 데이터셋 분포에 맞게 잘 설정되었는지 확인해보자.

Average forward time: 0.54 ms, Average NMS time: 0.43 ms, Average inference time: 0.97 ms
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.287
 Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.443
 Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.326
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
 Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.000
 Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.287
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.483
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.535
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.535
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = -1.000
 Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = -1.000
 Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.535

잘 보면 area=small과 area=medium에서만 -1.000이 표시되는 것을 확인할 수 있다. 간단하게 COCOeval의 areaRng parameter를 변경하여 문제를 해결할 수 있다. 넣어야 하는 값은 데이터셋 내 Bounding box의 distribution을 직접 분석하여 구하자. (여기에서 COCO 데이터셋 내 객체들은 small이 41%, medium이 34%, 나머지 large가 24%라고 한다.)

하지만 이렇게 되면 일반적으로 여러 논문에서 표시하는 $AP_S$, $AP_M$, $AP_L$ metric과 비교할 수 없게 된다는 문제점이 있으므로 주의하자.

# COCOeval 초기화 부분
...
cocoEval = COCOeval(cocoGt, cocoDt, annType)

# areaRng parameter를 변경한다.
obj_area_sm = 777268
obj_area_md = 93240
cocoEval.params.areaRng = [
    [0, 1e5**2],
    [0, obj_area_sm],
    [obj_area_sm, obj_area_md],
    [obj_area_md, 1e5**2],
]
...

Further Research

기본 COCO Python Evaluator COCOEval에서는 아래 3개의 기준으로 all, small, medium, large 크기를 구분한다. COCO paper에서는 이러한 언급이 별도로 없지만 COCO Evaluation API에서 해당 내용을 구현한다(원본 코드 보러가기)

  • all: $0$ ~ $10000^2$
  • small: $0$ ~ $32^2$
  • medium: $32^2$ ~ $96^2$
  • large: $96^2$ ~ $10000^2$

Custom COCO-style Dataset이 이러한 크기보다 훨씬 큰 Object만을 가지고 있다면 이중 일부는 -1.000으로 표시되는 것으로 파악된다.

[Kubernetes] PyTorch 학습 시 Pod의 빠른 종료를 위한 yaml 설계 방법

[새로운 방법 발견!]

아래 방법을 계속 사용하다가, yaml에 직접 넣어서 사용할 수 있는 구문을 찾았습니다.

spec:
  terminationGracePeriodSeconds: 0

해당 구문을 이용하여 Pod delete시 바로 중단 및 삭제되도록 할 수 있습니다.


기존 방법으로는 kubectl delete 시 --force --grace-period=0 옵션을 주어야만 강제 종료되고, 학습 process에 실제로 Ctrl+C를 주는 것과 동일하게 SIGTERM 을 보낼 수는 없었습니다. 이러한 문제점은 학습 프로세스의 종료시 callback들 (wandb 등)이 정상 작동하지 않는다는 문제점이 있습니다.이를 해결할 수 있는 방안을 소개 드립니다.
다만 아래와 같은 몇 가지 drawback이 있으므로 참고하여주시고, 더 좋은 방법이 있다면 공유 부탁드립니다.

  1. pip install 이 불가능하므로 미리 requirements.txt를 설치한 image를 준비해야 함
  2. runAsUserrunAsGroup 을 1003으로 고정해야 하므로 HOME 변수를 /workspace로 고정해야만 사용 가능

기존 방식

apiVersion: v1
kind: Pod
metadata:
  name: jungin500-mobilenetv2
spec:
  securityContext:
    runAsUser: 0
    runAsGroup: 0
    fsGroup: 1003

  restartPolicy: Never

  volumes:
    - name: shmdir
      emptyDir:
        medium: Memory
    - name: pvc-volume
      persistentVolumeClaim:
        claimName: lab-pvc

  containers:
    - name: gpu-container
      image: ghcr.io/jungin500/mobilenet_v2
      volumeMounts:
        - mountPath: /dev/shm
          name: shmdir
        - mountPath: /home/lab
          name: pvc-volume
      env:
        - name: TZ
          value: Asia/Seoul
      command:
        - "/bin/sh"
        - "-c"
      args:
        - >-
          set -x &&
          groupadd -g 1003 lab &&
          useradd -m -d /workspace -s /bin/bash -u 1003 -g lab lab &&
          runuser -u lab -- git clone https://jungin500:*****@github.com/jungin500/mobilenet_v2 mobilenet_v2 &&
          cd /workspace/mobilenet_v2 &&
          runuser -u lab -- git checkout 7d198633d19c2005e22b118ba27082eca3f2846a &&
          runuser -u lab -- pip3 install -r requirements.txt &&
          runuser -u lab -- /bin/bash -c "mkdir -p /home/lab/jungin500/mobilenet_v2_220718 || true" &&
          runuser -u lab -- python3 train.py ****
      securityContext:
        allowPrivilegeEscalation: false

      resources:
        requests:
          nvidia.com/gpu: 1
        limits:
          nvidia.com/gpu: 1

변경된 방식

apiVersion: v1
kind: Pod
metadata:
  name: jungin500-shutdown-signal-test
spec:
  securityContext:
    # [1] runAsUser, runAsGroup을 Fix
    runAsUser: 1003
    runAsGroup: 1003
    fsGroup: 1003

  restartPolicy: Never

  volumes:
    - name: shmdir
      emptyDir:
        medium: Memory
    - name: workdir
      emptyDir: {}
    - name: pvc-volume
      persistentVolumeClaim:
        claimName: lab-pvc

  # [2] git clone, 학습 결과물 폴더 링크 세팅등을 initContainers에서 진행
  # 이 때, pip install 등은 불가능하므로 미리 requirements.txt를 설치한 image를 준비해야 함
  initContainers:
    - name: clone-directory-set
      image: alpine/git
      volumeMounts:
        - mountPath: /workspace
          name: workdir
        - mountPath: /home/lab
          name: pvc-volume
      workingDir: /workspace
      env:
        - name: TZ
          value: Asia/Seoul
        - name: HOME
          value: /workspace
      command:
        - "/bin/sh"
        - "-c"
      args:
        - >-
          set -x &&
          git clone https://jungin500:***@github.com/jungin500/mobilenet_v2 mobilenet_v2 &&
          cd /workspace/mobilenet_v2 &&
          git checkout 7d198633d19c2005e22b118ba27082eca3f2846a &&
          mkdir -p /home/lab/jungin500/mobilenet_v2_test || true
  
  # [3] 기타 작업 (폴더 생성, 이동 등)은 불가능함
  #     argument도 string의 형태가 아닌 array로 주어야 작동함
  containers:
    - name: gpu-container
      image: ghcr.io/jungin500/mobilenet_v2
      volumeMounts:
        - mountPath: /dev/shm
          name: shmdir
        - mountPath: /workspace
          name: workdir
        - mountPath: /home/lab
          name: pvc-volume
      workingDir: /workspace/mobilenet_v2
      env:
        - name: TZ
          value: Asia/Seoul
        - name: HOME
          value: /workspace
      command:
        - "/opt/conda/bin/python3.8"
        - "train.py"
      args:
        - "--arg1"
        - "arg1_value"
      securityContext:
        allowPrivilegeEscalation: false

      resources:
        requests:
          nvidia.com/gpu: 1
        limits:
          nvidia.com/gpu: 1

구체적으로 변경되어야 하는 부분은 다음과 같습니다.

spec.securityContext.runAsUser
spec.securityContext.runAsGroup
spec.volumes.name[workdir]
spec.initContainers
spec.containers.volumeMounts[mountPath=/workspace]
spec.containers.env[name=HOME]
spec.containers.command
spec.containers.args