데이터셋 라벨링·검수·훈련 배정 서비스

여러 과제의 이미지 데이터셋을 프로젝트 단위로 라벨링·검수하고, 검수 결과로 스냅샷을 만들어 훈련 서버에 자동 배정하는 사내 웹 서비스입니다.

연표로 돌아가기
문제
과제마다 라벨링 도구를 따로 운영했고, 운영으로 검증된 기존 도구도 단일 데이터셋을 전제로 만들어져 다른 과제에 그대로 쓸 수 없었습니다.
담당
설계 문서 작성부터 검수 API 와 상자 편집기, 통계 화면, 스냅샷과 통합 훈련 큐를 다루는 스케줄러, 훈련 서버 에이전트와 러너, 컨테이너 배포까지 맡았습니다.
설계 판단
훈련이 성공해도 자동 라벨링 모델은 명시적으로 승격할 때만 바꾸게 했습니다. 성능이 떨어진 모델이 자동 라벨에 쓰이면 데이터 품질이 함께 나빠지기 때문입니다.
검증 기준
완료로 보기 전에 운영 배포본을 실제 브라우저에서 실데이터로 확인하고, 화면 표시 여부는 속성값이 아니라 실제로 그려진 크기로 판정합니다. 실데이터에는 되돌릴 수 있는 조작만 시험한 뒤 원래대로 돌립니다.
결과 범위
사내 서비스로 운영하며 여러 과제의 프로젝트와 여러 대의 훈련 서버를 하나의 큐로 묶었습니다. D-FINE 계열 실훈련과 명시적 모델 승격까지 연결했고, 두 번째 검출 아키텍처인 YOLO26 러너도 운영 훈련 서버에서 확인했습니다. 남은 범위는 최신 세대 GPU 호스트의 실훈련 확인, 이 모델을 자동 라벨 모델로 쓰는 경로, 호스트 밖 백업 사본입니다.

과제마다 흩어져 있던 데이터셋 라벨링과 검수 과정을 하나의 사내 웹 서비스로 모았습니다.

검수를 마친 결과로 학습용 스냅샷을 만들고, 훈련 서버를 배정한 뒤 결과를 회수하는 흐름까지 한 서비스 안에서 이어집니다.

인증·검수·스냅샷·배정·결과 회수로 이어지는 흐름을 먼저 동작시킨 뒤, 프로젝트별 역할 권한과 실제 훈련 러너, 명시적 모델 승격을 이어 붙였습니다.

자동 훈련은 새로 검수된 장수뿐 아니라 이미 쓴 이미지의 라벨 수정량도 변화량으로 세어, 재검수로 라벨 품질만 바뀐 경우에도 훈련이 등록되게 했습니다.

검수 배정은 클래스와 속성 값마다 완료분 확보량이 적을수록 커지는 연속 가중치를 매기고, 이미지가 담은 값의 가중치를 합산한 점수 순으로 뽑습니다. 동점은 무작위이고 점수가 0 인 이미지도 뒤쪽에서 배정해 굶기지 않습니다. 처음에는 평균의 절반 미만이면 우선으로 보는 이분법이었으나 표본의 대부분이 우선으로 판정되어 변별력이 없어 교체했습니다. 단독 가중은 낮아도 묶으면 취약한 클래스·속성 조합은 모델 진단의 F1 에서 낸 배수를 정책 태그로 가중하며, 조합의 취약 여부는 확보량이 아니라 모델 진단으로만 알 수 있어 자동으로 산출하지 않았습니다.

검수 완료는 서버가 속성 결측과 관계 규칙 위반(부모 상자 안에 자식 상자, 같은 축 값의 일치, 한 상자 안의 금지 조합)을 검사해 거절합니다. 저장은 전량 치환이고 서버가 좌표를 0~1 로 자르고 면적이 0 인 상자를 버리며 추가·삭제·이동·클래스 변경량을 직접 계산해 이벤트 원장에 남깁니다. 규칙은 프로젝트 설정에 두고, 러너가 모르는 규칙 종류는 다른 식으로 세지 않고 건너뛰게 해 훈련 뒤 평가도 같은 규칙으로 준수율을 냅니다. 한계도 적었습니다. 검사는 완료 시점에 하므로 동시 편집 충돌은 막지 못하고, 저장이 전량 치환이라 상자 식별자가 매번 바뀌어 이동·클래스 변경 집계는 근사값입니다.

훈련용 스냅샷은 검수 완료 이미지만 담고, 제외 이미지와 끈 클래스의 상자, 디스크에 없는 파일을 뺍니다. 클래스 인덱스를 다시 매기지 않고 대응표를 메타에 기록하며 이미지·라벨·분할 목록을 한 압축 파일에 묶어, 어떤 이미지가 어디에 쓰였는지 사후에 감사할 수 있게 했습니다. 런의 훈련 파라미터는 배정 시점의 프로젝트 설정 사본을 고정하고, 훈련 상세의 데이터 구성 통계도 현재 DB 가 아니라 스냅샷에서 읽습니다. 스냅샷 이후에도 검수가 계속되어 분포가 달라지기 때문입니다. 훈련·검증·시험 분할은 클래스와 속성 값을 층으로 삼아 희소한 층부터 채우는 근사 방식이고 파일 이름 해시로 순서를 고정해 재현됩니다. 검증셋을 고정하지 않으면 같은 데이터를 다르게 나눈 두 런의 클래스별 AP 가 흔들려 파라미터 변경 효과와 분할 잡음을 구분할 수 없어 고정 목록으로 묶을 수 있게 했고, 합성·이관 이미지는 훈련에만 고정해 지표가 합성물 성능으로 부풀지 않게 했습니다.

자동 훈련은 마지막 성공 런의 스냅샷을 기준선으로 신규 완료 장수, 재검수로 바뀐 장수, 라벨 변화량, 신규 클래스를 세고 쿨다운과 최소 신규분 게이트를 통과해야 등록되며, 걸린 사유와 수치를 런에 기록해 왜 지금 걸렸는지 사후에 확인할 수 있습니다. 신규분이 적은데도 쿨다운마다 훈련이 나가던 원인은 신규 클래스 사유가 기준선과 무관하게 항상 참이던 결함이어서, 기준선의 클래스 집합과 대조하도록 고치고 사유와 별개로 신규분 하한을 게이트로 두어 회귀 시험을 남겼습니다. 모델 승격은 관리자 이상이 성공한 런 가운데 산출물이 서비스에 저장된 것만 지정할 수 있고, 직전 활성 모델 대비 mAP 하락과 클래스별 신뢰도 임계값 변동을 함께 보여 줍니다. 자동 라벨용 임계값은 훈련이 끝날 때마다 검증셋에서 다시 산출하며 재현율을 먼저 지킵니다. 검수자가 잘못 붙은 상자를 지우는 쪽이 없는 상자를 그리는 쪽보다 빠르기 때문입니다.

배정 단위는 장치가 아니라 서버라서 서버당 한 번에 한 훈련만 올리고, 그 훈련이 서버의 쓸 수 있는 GPU 를 모두 써 `torchrun` 으로 돌립니다. 후보에서 빠진 서버에는 오프라인, 수신 중지, 아키텍처 미지원, 훈련 진행 중, 가용 장치 없음, VRAM 부족 중 사유를 표시합니다. 외부 점유는 점유율 추정이 아니라 에이전트가 자기 프로세스 집합과 대조해 보고하는데, 컨테이너 안의 프로세스 번호와 드라이버가 알려 주는 호스트 번호가 달라 자기 훈련을 외부 점유로 보고하던 오류를 고쳤습니다. 서비스가 훈련 서버로 들어가지 않고 에이전트가 작업을 가져가는 방식이라 방화벽은 단방향이면 되고, 하트비트가 끊기면 오프라인으로, 더 오래 끊기면 stale 로 표시하되 자동으로 다시 큐에 넣지 않습니다. 네트워크만 끊긴 살아 있는 훈련이 중복 실행되면 GPU 메모리 부족이 나기 때문입니다. 정상 훈련이 stale 로 찍히던 문제는 에이전트가 훈련을 기다리는 동안 하트비트 루프가 멈춘 것이 원인이어서, 훈련을 별도 태스크로 띄우고 서비스가 진행 보고를 받을 때도 하트비트를 갱신하도록 두 겹으로 고쳤습니다.

훈련 러너를 실데이터로 돌리며 조용히 틀리던 것을 차례로 고쳤습니다. 사전학습 가중치가 전달되지 않아 헤드가 무작위 초기화로 학습되던 것, 총 에폭 설정 키가 원본의 철자 오타라 지정해도 무시되던 것, 지수 표기 학습률이 설정 파일에서 문자열이 되어 시작 즉시 실패하던 것, 디코더 활성함수가 사전학습 가중치와 달라 지표가 0 에서 움직이지 않던 것이 그 예입니다. 멀티 GPU 훈련이 첫 에폭에서 오래 매달리던 원인은 백본 가중치 캐시가 없을 때 한쪽 프로세스만 예외로 죽고 다른 쪽이 집합 통신 제한 시간까지 기다리는 것이어서, 러너가 훈련 전에 캐시를 보장하도록 했습니다. 속성 헤드가 붙은 모델의 ONNX 는 내보내기 직전에 후처리 출력과 대조해 다르면 내보내기를 실패시킵니다. 속성이 다른 상자에 붙는 사고는 겉으로 드러나지 않기 때문입니다.

두 번째 검출 아키텍처(YOLO26)용 러너는 운영 훈련 서버에서 2-GPU 실훈련, 조기 종료, 진행 보고, 산출물 회수, ONNX 와 체크포인트 출력 대조까지 확인했습니다. 검증분이 없으면 훈련 이미지를 검증에 재사용하지 않고 실패시키고, 가중치를 자동으로 내려받지 않으며, 희소 클래스 번호는 학습용 연속 번호로 바꾸되 평가와 메타에는 원본 번호를 보존합니다. 운영 검증 중에는 평가 키에 COCO 용 +1 을 잘못 적용하던 오류 등을 찾아 고쳤고, 경로 오류가 있던 런은 검증 근거에서 제외했습니다. 구현 완료와 운영 GPU 검증을 구분해, 최신 세대 GPU 호스트의 실훈련은 호스트에 접속하지 못해 검증하지 못했고 이를 호환 확인으로 해석하지 않았습니다. 이 모델의 ONNX 는 기존 자동 라벨 도구의 출력 규약과 달라 자동 라벨 모델로 승격하지 않았습니다.

서비스 안의 파일 업로드와 영상 프레임 추출(간격과 최대 장수에 상한)은 SHA256 과 지각 해시(32x32 이산 코사인 변환의 저주파)로 중복을 걸러 CPU 추론 프리라벨을 붙여 검수 대기에 올립니다. 작업은 PostgreSQL 작업 테이블과 단일 스케줄러의 비동기 워커로 처리해 Redis 큐와 별도 워커 컨테이너를 쓰지 않았고, 재기동하면 실행 중이던 작업은 대기로 되돌리고 재시도는 이미 처리한 프레임을 보존해 같은 프레임이 두 번 등록되지 않게 했습니다. 모델이 지정됐는데 산출물이 없거나 추론이 실패하면 빈 상자로 두지 않고 작업 실패로 기록합니다. 합성 이미지 후보는 원본과 근접 중복으로 판정된 것을 건너뛰었고, 원본과 합성 쌍이 분할 경계를 넘으면 평가 누수가 생길 수 있기 때문입니다.

자동 테스트는 16개 파일 96개(서비스 11개 파일 78개, 훈련 서버 4개 파일 15개, 화면 목록 로직 1개 파일 3개, 실행하지 않은 정적 집계)이고 CI 는 없으며, 실행 중인 서비스를 대상으로 하는 수동 스모크 3종을 따로 둡니다. 다수가 쿠키 수명, 규칙 종류 건너뛰기, 조기 종료 경계, 설정 저장 오류 같은 실제 사고의 회귀 시험입니다. 배포는 이미지를 로컬에서 만들어 파일로 옮겨 적재하고, 다른 작업이 미커밋 코드를 끼워 넣지 못하도록 커밋된 트리에서 빌드하며, 배포 여부는 로컬·서빙·원격 소스의 파일 해시 일치로 판정합니다. 자동 복구는 세 겹입니다. 스케줄러가 사이클마다 생존 신호 파일을 갱신해 헬스체크가 나이를 보고, 사이클이 상한을 넘으면 프로세스가 스스로 종료해 재시작 정책에 맡기고, 라벨이 붙은 컨테이너만 감시하는 자동 재기동기가 비정상 컨테이너를 되살립니다. DB 백업은 매일 논리 덤프를 만들고, 복원 리허설 도구가 새 백업을 임의 이름의 임시 DB 에 복원해 모든 테이블의 행 수와 마이그레이션 리비전, 활성 모델 참조를 대조합니다. 같은 호스트의 별도 경로로 분리했을 뿐 호스트 밖 사본은 아직 없습니다.

화면

프로젝트 카드와 검수 진행률, 클래스 분포 막대, 훈련 큐 표가 있는 대시보드 화면프로젝트 카드와 검수 진행률, 클래스 분포 막대, 훈련 큐 표가 있는 대시보드 화면
대시보드. 프로젝트별 검수 진행률과 클래스 분포, 훈련 큐를 한 화면에서 봅니다.
이미지 위에 클래스별 색 상자가 그려지고 왼쪽에 클래스 목록, 위에 실행취소·확대 도구가 있는 상자 편집 화면이미지 위에 클래스별 색 상자가 그려지고 왼쪽에 클래스 목록, 위에 실행취소·확대 도구가 있는 상자 편집 화면
상자 편집기. 클래스별로 상자를 보이거나 감추고, 실행취소를 100단계까지 되돌립니다.

더미 데이터로 채운 그림입니다. 실제 화면과 배치·문구가 다를 수 있습니다.

구조와 흐름

라벨링에서 훈련까지

완료제외반려새 모델로 다시 자동 라벨이미지 반입과제별 프로젝트 · 업로드 · 프레임 추출자동 라벨기존 모델로 선행 표시검수자 배정희소 클래스 가중 · 72시간 만료상자 편집 · 검수판정검수 완료데이터셋 제외반려다시 열면 검수 대기스냅샷 생성변화량 기준통합 훈련 큐서버 배정사용 가능한 GPU 전부 · 외부 점유 반영훈련 실행호스트 에이전트결과 회수진행률 · 지표 · 가중치
검수 결과가 쌓이면 스냅샷이 만들어지고, 훈련 중이 아닌 서버가 생기면 큐에서 꺼내 배정합니다. 반려한 이미지는 다시 열어야 검수 대기로 돌아갑니다.

서비스 구성

서비스 (Docker Compose)훈련 호스트작업 요청 · 상태 보고웹 UI대시보드 · 상자 편집기FastAPI인증 · 검수 · 통계스케줄러배정 · 큐PostgreSQL에이전트HTTP pull훈련 러너원본 이미지 저장소읽기 전용 마운트
서비스와 훈련 호스트는 서로 import 하지 않고, 공유 스키마만 계약 패키지로 둡니다.
FastAPIPostgreSQLDocker ComposePython
검출 모델 훈련·평가 지표 읽는 법