48시간 현장 기록: 스키마가 아닌 일치율(Agreement Rate)로 무료 모델 배치 작업을 제어하다
요약
AI 배치 작업의 신뢰성을 확보하기 위해서는 단순한 JSON 스키마나 종료 코드 검증만으로는 부족합니다. 필자는 모델이 조용히 인접 카테고리로 표류하는 '라벨 드리프트' 문제를 발견하고, 이를 방지하기 위해 배치가 시작되기 전에 작동하는 '골든 세트 일치율 게이트'를 제안합니다. 이 게이트는 재현율 등 상세 지표를 활용하여 데이터 품질 저하 시 작업을 사전에 차단하는 것이 핵심입니다.
핵심 포인트
- 스키마 검증만으로는 라벨 드리프트(Label Drift) 같은 미묘한 오류를 잡아내기 어렵다.
- 배치 작업 전, '골든 세트 일치율 게이트'를 구현하여 데이터 품질을 사전에 점검해야 한다.
- 전체 평균 점수보다 개별 레이블의 재현율(per-label recall)이 시스템 안정성에 더 중요하다.
- 게이트는 충분히 크고 다양한 꼬리 부분 샘플링으로 구성되어야 유효하다.
저는 6,400줄의 프로덕션 로그에 무료 모델 엔드포인트를 연결하는 데 48시간을 보냈고, 모든 응답은 유효한 JSON이었습니다.
그것이 문제였습니다. 검증기(validator)는 형태를 확인했고, 파이프라인(pipeline)은 종료 코드를 확인했으며, 둘 다 배치 작업이 성공했다고 알려주었습니다. 그러고 나서 제가 수동으로 40줄을 샘플링하여 발견한 것은 라벨 중 약 5분의 1이 조용히 인접 카테고리로 표류했다는 것이었고, 이는 저의 다운스트림 알림 시스템(downstream alerting)이 이틀 동안 노이즈로 학습되었다는 것을 의미했습니다.
따라서 이것은 제가 가장 먼저 작성했어야 할 검증에 대한 현장 기록입니다. 배치 작업 이후가 아니라, 배치가 실행되기 전에 작동하는 골든 세트 일치율 게이트(golden-set agreement gate) 말입니다.
실제로 시도한 것들
저는 지루하고 화려하지 않은 작업을 가지고 있었습니다: 로그 라인을 고정된 라벨 세트로 분류하여 적절한 런북(runbook)으로 라우팅하는 것이었습니다. 규칙 기반 버전은 300줄의 정규 표현식(regex)까지 늘어났고, 제가 더 이상 읽을 수 없을 정도로 잘못된 부분이 많았기 때문에, 모델이 더 저렴한 유지보수 경로처럼 느껴졌습니다.
저는 버릴 수도 있는 실험에 돈을 쓰고 싶지 않았는데, 이것은 바로 MonkeyCode의 무료 모델 액세스와 무료 서버 옵션이 제 제약 조건에 맞는 경우였습니다. 저는 무료 서버에서 분류기를 실행했고, 골든 세트는 제 노트북에 보관했으며, 전체 과정을 인프라가 아닌 이틀짜리 스파이크(spike)로 취급했습니다.
공개 고지: 본 기사는 MonkeyCode의 제품 홍보 활동의 일환으로 작성되었습니다.
아래 내용을 복사하기 전에, 토큰 허용량 및 서버 사양에 대한 현재 제한 사항은 프로젝트 자체 문서를 확인해 주세요. 제가 정확하게 유지할 수 없는 숫자를 인용하지 않을 것이기 때문입니다.
아티팩트: 배치 작업 이전에 실행되는 게이트
이 하네스(harness)는 표준 라이브러리 Python으로 약 120줄 분량입니다. 이는 수동으로 라벨링된 라인들의 golden.jsonl 파일을 읽고, 엔드포인트에 라벨을 요청한 다음, 일치율이 사용자가 설정한 최저선(floor) 아래로 떨어지면 배치가 시작되는 것을 거부합니다.
# gate.py — exit 1은 배치 작업을 실행하지 않음을 의미
import json, os, sys, urllib.request
...
이를 크론 작업(cron job)이나 사전 비행 단계(pre-flight step)로 실행하고, 그 종료 코드를 이미 파이프라인을 중지시키는 어떤 시스템에 연결하면 됩니다:
python gate.py || { echo "label drift: batch blocked"; exit 1; }
The per-label recall 라인이 전체 점수보다 더 중요합니다. rate_limit이 0.4인 상태를 숨긴 전체 0.86점은 균일한 커버리지를 가진 0.80점보다 나쁩니다. 왜냐하면 배치(batch)가 한 가지 브랜치에 자신감 넘치는 쓰레기 데이터(confident garbage)를 계속 공급할 것이기 때문입니다.
무엇이 고장났는지, 발생 순서대로
1. 나의 골든 세트(golden set)가 너무 작고 너무 깨끗했다
20개의 예제 중 2개가 schema로 레이블링되어 있었기 때문에, 단 하나의 누락(miss)이 재현율을 50포인트나 움직였습니다. 분포의 중간 부분이 아닌 보기 싫은 꼬리 부분에서 샘플링하여 약 80줄로 세트를 재구축하자, 이 게이트가 장식용이 아니라 유용한 것이 되었습니다.
2. 같은 입력에 대해 재시도 시 다른 레이블을 받았다
저는 온도를 0으로 설정하고 그것이 결정론적(deterministic) 특성을 의미한다고 가정했습니다. 그렇지 않았습니다. 호출이 실패하여 제가 재시도했을 때, 몇 줄의 레이블이 변경되었습니다. 제 원장(ledger)은 두 번째 답변이 존재했다는 기록 없이 그 답변을 저장했기 때문에, 드리프트는 제가 가지고 있던 모든 집계치에서 보이지 않았습니다.
key = hashlib.sha256(text.encode()).hexdigest()[:16]
ledger.write({"key": key, "state": "inflight", "attempt": n})
label = classify(text)
...
호출 전에 의도(intent)와 그 키를 기록함으로써, 눈에 보이지 않던 품질 문제가 쿼리 가능한 문제로 바뀌었습니다. 마침내 몇 개의 키가 하나 이상의 고유 레이블을 가졌는지 물어볼 수 있게 되었습니다.
3. 무료 서버가 배치 중간에 재시작되었다
극적인 일은 아니었지만, API 호출과 쓰기(write) 사이에 프로세스를 종료시킨 재시작이 있었습니다. 200줄이 분류되었고, 시간으로 비용을 지불했지만, 그 데이터는 사라졌습니다. 천 줄 단위가 아닌 라인별로 체크포인팅(checkpointing)을 적용한 것이 이를 해결했고, 저에게는 느린 디스크 쓰기 외에는 아무런 비용도 들지 않았습니다.
이 설정이 적절한 경우와 그렇지 않은 경우
| 상황 | 무료 엔드포인트 + 무료 서버 | 계약(규격)이 있는 것과 연동된 경우 |
|---|---|---|
| 일회성 스파이크 (2일), 삭제해도 됨 | 적합함 | 과도함 |
| ... |
만약 귀하의 작업이 지연 시간(latency) 예산이 있는 실시간 요청 경로이거나, 데이터가 네트워크를 벗어날 수 없다면, 이 전체 글은 적절하지 않습니다. 오프라인 분류(classification), 추출 작업(extraction passes), 그리고 잘못된 답변의 비용이 사고(incident)가 아니라 재실행(rerun)인 실험에 사용하십시오.
내가 반복할 것과 건너뛸 것
- 배치 작업을 시작하기 전에 게이트를 작성하라. 40분이 걸렸지만 2일을 절약해 주었을 것이다.
- 대표성이 있는 중간 데이터가 아닌, 보기 어려운(ugly) 입력 데이터의 10%를 포함하는 골든 세트(golden set)를 유지하라.
- 모든 레이블 옆에 시도 카운터(attempt counter)를 항상 저장하라.
- 첫날부터 영리한 청킹(clever chunking)은 건너뛰어라. 라인별 호출이 느렸지만 실패 모드(failure modes)가 읽기 쉬웠다.
내가 같은 스파이크를 다시 실행할까? 그렇다. 그리고 나는 게이트에 도착하는 대신, 게이트부터 시작할 것이다. 왜냐하면 스키마가 유효한 응답은 의미(meaning)에 대한 주장이 아니라 형식(form)에 대한 주장이기 때문이다.
위의 하네스(harness)는 의도적으로 지루하며 특정 공급업체에 종속되지 않기 때문에, 제공되는 무료 모델 액세스를 포함하여 OpenAI와 호환되는 엔드포인트 어디든 연결하여 자체 골든 세트를 통과하는지 확인할 수 있다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기