Isolation Forest 수도 사용 이상 탐지: JAGYEOKSU의 혼합 점수 설계
Isolation Forest 수도 사용 이상 탐지는 시간별 사용량을 모델에 넣고 위험 여부를 곧바로 판정하는 문제가 아니다. JAGYEOKSU는 수도 사용 이력을 23개 특징으로 요약한 뒤 고립 트리의 ML 점수, 통계 점수, 규칙 점수를 결합한다. 그 결과도 건강이나 생존 상태가 아니라 담당자가 먼저 확인할 후보의 순서로 제한한다.
이 글은 2026년 8월 5일 기준 JAGYEOKSU 커밋 32739c1의 특징 생성기, 모델, 서비스, 평가 문서를 따라 이 설계를 해석한 프로젝트 딥다이브다. Python 3.12.9 환경에서 app과 scripts의 문법 컴파일은 통과했지만, 조사 환경에 pytest와 scikit-learn 의존성이 없어 모델 학습과 공개 평가를 다시 실행하지는 못했다. 따라서 성능 수치는 직접 재현값이 아니라 저장소가 공개한 평가 기록으로 구분해 다룬다.
고립 트리는 데이터를 빨리 혼자 남게 만드는 트리다
고립 트리(Isolation Tree)는 정상 데이터의 중심이나 이상 데이터의 정답을 먼저 학습하는 분류 트리가 아니다. 현재 노드에서 특징 하나를 무작위로 고르고, 그 특징의 최솟값과 최댓값 사이에서 분할값 하나를 무작위로 뽑아 데이터를 계속 둘로 나눈다. 표본 하나만 남거나 깊이 제한에 도달하면 분할을 멈춘다.
루트에서 한 표본이 혼자 남은 노드까지 거친 간선 수가 경로 길이(path length)다. 정상 표본은 비슷한 이웃이 많아 혼자 남기까지 여러 번 나뉘는 경향이 있다. 반면 이상 표본은 대체로 수가 적고 특징값이 달라 몇 번만 잘라도 고립될 가능성이 높다. 따라서 평균 경로가 짧을수록 더 이례적인 표본으로 본다. 이것이 원 논문이 제시한 핵심 아이디어다.
한 트리의 무작위 분할은 운에 따라 결과가 달라질 수 있다. Isolation Forest는 이런 고립 트리를 여러 개 만들고 각 표본의 경로 길이를 평균해 우연을 줄인 앙상블이다. 즉 tree는 한 번의 무작위 격리 과정이고, forest는 그 판단을 모은 집합이다. scikit-learn의 decision_function은 이 원리를 점수로 제공하되 방향은 낮을수록 비정상이며, 0 미만을 outlier로 분류한다. (scikit-learn 공식 API)
설명용으로 하루 수도 사용량 특징이 [90, 95, 97, 100, 102, 105, 0]이라고 해보자. 첫 트리가 0과 105 사이에서 분할값 40을 뽑으면 0은 한 번의 분할로 혼자 남는다. 90~105의 값은 같은 가지에 몰려 있어 95나 100 같은 추가 경계를 여러 번 지나야 각각 분리된다. 다른 트리가 처음부터 98을 고르면 0이 즉시 혼자 남지는 않지만, 다음 경계가 50이라면 두 번째 분할에서 고립된다. 여러 트리에서 이 과정이 반복되면 0의 평균 경로가 정상 군집보다 짧아진다.
이 숫자는 작동 원리를 설명하기 위한 가상 예시이며 JAGYEOKSU를 실행해 얻은 재현 결과가 아니다. 더 중요한 제한도 있다. 모델은 0이 드물다는 사실만 포착할 뿐, 그 원인이 위험 상황인지 장기외출인지 빈집인지 센서 장애인지는 알지 못한다. 고립 트리는 원인 판정기가 아니라 이례적인 표본을 먼저 살펴보게 하는 신호다.
정답 라벨이 부족할 때 후보를 정렬하기 위해 쓴다
수도 사용 이상처럼 실제 사건이 드물고 원인까지 확정한 라벨을 대량으로 모으기 어려운 문제에서는 지도학습 분류기를 바로 만들기 어렵다. Isolation Forest는 정상·이상 클래스 라벨 없이 수치 특징 행렬만으로 경로 구조를 학습하고 새 표본의 이상 점수를 계산할 수 있다. 그래서 “무슨 사건인지 자동 확정”하는 모델보다 사람이 확인할 후보를 정렬하는 초기 기준선으로 쓰기 좋다.
거리나 이웃 밀도를 매번 직접 계산하지 않고, 작은 부분표본과 무작위 트리를 이용하는 점도 실용적이다. 다만 라벨이 필요 없다는 말은 평가가 필요 없다는 뜻이 아니다. 시간 순서를 보존한 홀드아웃, 합성 이상 시나리오, 사람이 확인한 정상 표본으로 오탐과 미탐을 측정해야 한다. LOF처럼 국소 밀도를 보는 방법, 정상분포를 가정하는 공분산 모델, 경계를 학습하는 One-Class SVM과도 실제 데이터에서 비교해야 한다.
JAGYEOKSU에서 기억할 mental model은 다음과 같다.
시간별 수도 사용 이력
→ 평가시각 이전 데이터만 선택
→ 23개 시계열 특징 생성
→ Isolation Forest ML 점수
→ 통계 점수·명시적 규칙 점수와 결합
→ 데이터 오류 우선 분기
→ NORMAL / OBSERVE / CHECK_REQUIRED
→ 담당자의 맥락 확인

여기서 모델은 전체 흐름의 한 부분이다. 무사용, 결측, 누수처럼 업무 의미가 분명한 패턴을 모두 고립 트리에 떠넘기지 않고 통계와 규칙으로 보완한다. 데이터 품질이 나쁘면 점수 계산보다 오류 상태를 먼저 내보낸다. 이 경계가 없다면 “수도 데이터가 오지 않았다”와 “수도를 쓰지 않았다”가 같은 위험 신호로 섞일 수 있다.
시계열을 23개 특징으로 바꾸는 이유
고립 트리에 들어가는 한 행은 원래의 시간별 사용량 한 건이 아니다. FeatureGenerator는 계량기별 이력을 평가시각 이하로 자른 뒤 23차원 표본으로 만든다. 모든 시간 창은 (endpoint - window, endpoint] 범위를 사용한다. 저장소의 test_no_future_data_leakage는 평가시각 뒤 사용량을 큰 값으로 바꿔도 특징이 변하지 않아야 한다는 조건을 둔다.
23개 이름을 외우는 것보다 네 가지 질문으로 묶어 보는 편이 구조를 이해하기 쉽다.
| 질문 | 대표 특징 | 설계 의도 |
|---|---|---|
| 최근에 얼마나 썼나 | 최근 6·12·24시간 합, 직전 24시간 합, 최근 24시간 최대값 | 갑작스러운 감소·증가와 단기 변화를 본다 |
| 이 계량기의 평소와 얼마나 다른가 | 7·30일 평균, 30일 중앙값·표준편차, 사용 변화율 | 다른 가구가 아닌 개인 기준선과 비교한다 |
| 생활 리듬이 끊기거나 이어졌나 | 마지막 사용 후 경과시간, 0·저사용·양수 사용 연속시간, 첫 사용 시각·지연 | 장기 무사용, 지속 누수, 생활 시간대 변화를 표현한다 |
| 데이터와 분포를 믿을 수 있나 | 결측률, 사용 엔트로피, 변동계수, 같은 요일·시각 편차 | 미수신과 주기적 패턴, 분포 변화를 분리한다 |
최근 사용량만 더하면 0이 된 이유를 알 수 없다. 실제 무사용일 수도 있고, 수집 장애일 수도 있으며, 평소부터 사용량이 적은 계량기일 수도 있다. 개인 기준선과 결측 특징을 함께 만드는 이유다.
구현에는 주의해서 읽어야 할 부분도 있다. zero_usage_streak_hours와 저사용 연속시간 계산은 fillna(0)을 사용하므로 특징 계층만 놓고 보면 결측이 무사용 연속 구간에 포함될 수 있다. 서비스가 최근 24시간 개인 결측률 30% 이상 또는 여러 계량기의 동시 누락 80% 이상일 때 DATA_ERROR를 우선하는 것이 중요해진다.
same_weekday_deviation이라는 이름도 직관과 구현이 완전히 같지는 않다. 코드는 최근 4주 같은 요일의 일별 합계가 아니라 해당 요일에 걸린 시간별 사용값 가운데 마지막 네 값을 참조해 최근 24시간 합과 비교한다. 변수 이름만 보고 “4주 같은 요일 일합 비교”라고 해석하면 실제 동작을 과장하게 된다.
세 개의 점수는 서로 다른 실패를 보완한다
WaterAnomalyModel은 RobustScaler와 다음 고립 트리 설정을 사용한다.
| 파라미터 | 값 | 해석 |
|---|---|---|
n_estimators |
200 | 무작위 격리 트리 수 |
contamination |
0.01 | 학습 점수에서 outlier 경계를 정하는 이상치 비율 가정 |
random_state |
42 | 반복 실행의 무작위성을 재현하기 위한 seed |
n_jobs |
-1 | 모델 학습에 사용 가능한 프로세서를 활용 |
RobustScaler는 이 프로젝트가 특징 단위 차이와 극단값을 다루기 위해 택한 전처리다. 고립 트리는 거리 기반 모델이 아니므로 스케일링이 이론적으로 반드시 필요하다고 말할 수는 없다. scaler 사용 전후를 같은 데이터로 비교한 ablation도 공개 기록에 없으므로, 효과는 별도 검증 대상으로 남는다.
scikit-learn의 decision_function은 낮을수록 더 비정상적이다. JAGYEOKSU는 학습 점수의 1% 분위수 q01과 99% 분위수 q99를 저장하고 방향을 뒤집어 ML 점수를 만든다.
ml_anomaly_score = clip(
100 * (q99 - decision_score) / max(q99 - q01, 1e-9),
0,
100,
)
이 값은 위험 확률이 아니다. 학습 분포가 달라지면 같은 원점수도 다른 0~100 값이 될 수 있고, 두 분위수 밖의 값은 0이나 100에 포화된다. contamination=0.01, ML 점수 50, 서비스 최종점수 45·70은 각각 다른 경계이므로 “위험 가능성 1%” 같은 하나의 의미로 합쳐서는 안 된다.
서비스는 이 ML 점수에 통계와 규칙을 더한다.
final_score = clip(
0.45 * ml_anomaly_score
+ 0.35 * statistical_score
+ 0.20 * rule_score,
0,
100,
)
통계 점수는 같은 요일·시각 편차, 사용량 급감, 변동계수 가운데 가장 큰 신호를 비선형 변환한다. 규칙 점수는 개인의 과거 최장 무사용을 넘는 상황, 80% 이상 급감, 12시간 이상 연속 양수 사용, 급증, 동일 양수값 반복처럼 설명 가능한 조건을 더한다. 최종점수 70 이상은 CHECK_REQUIRED, 45 이상은 OBSERVE, 그 미만은 NORMAL이다.
가중치 0.45/0.35/0.20과 임계치 45/70이 현장의 오탐 비용이나 확인 인력에 맞게 보정됐다는 독립 증거는 아직 없다. 따라서 이 숫자는 보편 기준이 아니라 현재 구현의 운영 가설로 봐야 한다.
장점은 빠른 기준선, 단점은 의미를 모른다는 점이다
Isolation Forest의 장단점은 모델 일반론으로 끝나지 않는다. JAGYEOKSU에서는 각각 다음 운영 판단으로 이어진다.
| 장점 | 왜 유용한가 | JAGYEOKSU에서의 의미 |
|---|---|---|
| 라벨 없이 학습 가능 | 확정 이상 정답이 없어도 이례적인 표본의 순서를 만들 수 있다 | 실제 건강 위험 라벨이 부족한 초기 후보 생성에 맞는다. 합성 평가는 실제 건강 outcome 검증을 대신하지 않는다 |
| 거리·밀도 계산이 불필요 | 무작위 분할과 부분표본으로 트리를 만든다 | 11,400개 특징 표본과 계량기 확대를 다룰 실용적인 기준선이 된다. 실제 처리량 벤치마크는 별도 필요하다 |
| 여러 분포 모양에 적용 가능 | 정상 데이터를 하나의 타원형 정규분포로 고정하지 않는다 | 생활 패턴이 하나의 모양으로 모이지 않을 수 있는 수도 특징에 출발점으로 쓸 수 있다 |
| 새 표본 점수화가 단순함 | 새 특징 벡터를 학습된 트리들에 통과시키면 된다 | 평가시각마다 생성한 23개 특징을 배치로 점수화하는 흐름과 맞는다 |
| 연속 점수를 제공함 | 이진 판정 외에 검토 우선순위를 만들 수 있다 | ML 점수를 통계·규칙 점수와 결합할 수 있다. 다만 0~100은 확률이 아니다 |
| 무작위성을 재현할 수 있음 | 같은 데이터·버전에서 seed를 고정해 회귀를 비교할 수 있다 | random_state=42가 테스트에 도움을 준다. 데이터 기간과 라이브러리 버전도 함께 고정해야 한다 |
단점은 파라미터 하나로 해결되지 않는다.
| 단점·주의점 | 실제 실패 방식 | JAGYEOKSU에서 필요한 보완 |
|---|---|---|
contamination에 따른 경계 변화 |
예상 이상 비율이 실제와 다르면 오탐·미탐 균형이 달라진다 | 0.01을 위험 확률로 읽지 않고, 확인 가능한 건수와 오탐 비용으로 재보정한다. ML 50과 최종 45·70 경계도 별개로 관리한다 |
| 원시 시계열 순서를 이해하지 못함 | 시간 순서, 계절성, 연속 무사용의 의미를 모델이 스스로 배우지 않는다 | 최근 합계, 무사용 길이, 개인 기준선, 같은 시간대 편차 같은 특징 엔지니어링과 미래 데이터 차단이 필요하다 |
| 희귀 정상 패턴도 이상으로 탐지 | 장기외출·이사·명절 같은 정상 변화도 짧은 경로를 만들 수 있다 | 정상 장기외출 구분 0%라는 공개 결과를 받아들이고, 외부 맥락과 사람 확인 없이 자동 조치하지 않는다 |
| 설명 가능성이 제한됨 | 수백 트리의 평균 경로는 “왜 위험한가”를 단일 규칙으로 설명하지 못한다 | 원본 특징값과 통계·규칙 근거를 함께 보여준다. 자체 permutation 지표도 인과 설명으로 과장하지 않는다 |
| 데이터 drift에 민감 | 계절, 계량기 교체, 가구 구성 변화로 같은 행동의 점수 위치가 달라진다 | 기간별 점수 분포·경보율·결측률을 감시하고 시간 순서 외부검증 후 재학습한다 |
| 축 정렬 분할과 고차원 상관 | 한 번에 특징 하나만 자르므로 강하게 상관된 비스듬한 정상 영역을 비효율적으로 나눌 수 있다. 무관 특징이 늘면 신호도 희석될 수 있다 | 23개 특징을 계속 늘리기보다 시나리오별 ablation, seed 안정성, 다른 모델 비교를 한다. 필요하면 Extended Isolation Forest도 검토한다 |
| 범주형 값을 그대로 받지 못함 | 임의 정수 인코딩은 가짜 순서를 만들고 고카디널리티 one-hot은 차원을 키운다 | 현재 23개 수치 특징에는 직접 문제가 없다. 지역·계량기 유형을 추가한다면 명시적 인코딩과 미지·희귀 범주 처리를 검증한다 |
| 이상이 흔하거나 서로 뭉치면 전제가 약해짐 | 이상이 “적고 다르다”는 조건이 깨지면 빨리 고립되지 않을 수 있다 | 대규모 단수나 통신 장애는 개별 ML 점수보다 여러 계량기 동시 누락 분기로 먼저 처리한다 |
이 표에서 특히 놓치기 쉬운 것은 특징 엔지니어링이다. Isolation Forest가 비지도 모델이어도 시계열의 의미까지 자동으로 만들어주지는 않는다. 어떤 시간 창을 쓸지, 개인 기준선과 비교할지, 0과 결측을 어떻게 분리할지가 모델 선택보다 결과를 크게 바꿀 수 있다.
공개 평가는 성공보다 실패 시나리오를 더 많이 말해준다
저장소 문서에 따르면 K-water 2023년 상반기 익명 스마트미터 자료를 시간 순서로 나누고, 앞선 67%를 학습에 사용했다. 학습 구간은 289,400행과 11,400개 특징 표본, holdout은 142,600행으로 기록돼 있다. 원본에는 실제 건강 이상 정답 라벨이 없으므로 holdout 복사본에 10종의 합성 시나리오를 하나씩 주입하고, 20개 seed로 총 200건을 평가했다.
이번 조사에서는 K-water 원본 Parquet와 저장된 모델 artifact가 없어 이 평가를 재실행하지 못했다. 아래 값은 JAGYEOKSU 공개 평가 문서에 기록된 결과다.
| 공개 평가 항목 | 기록값 |
|---|---|
| Precision | 0.862 |
| Recall | 0.728 |
| F1 | 0.781 |
| ROC-AUC | 0.300 |
| PR-AUC | 0.875 |
| 24·48·72시간 무사용 탐지 | 각각 100% |
| 데이터 누락 / 3시간 급증 탐지 | 각각 100% |
| 연속 누수 / 동일값 고정 탐지 | 50% / 35% |
| 80% / 95% 사용량 감소 탐지 | 30% / 40% |
| 정상 장기외출 정상 구분 | 0% |
좋은 숫자만 고르면 Precision 0.862와 PR-AUC 0.875가 눈에 띈다. 그러나 ROC-AUC는 0.300이고 정상 장기외출을 정상으로 구분한 비율은 0%다. 완만한 감소와 동일값 고정도 약하다. 10개 시나리오 가운데 양성 9개, 음성 1개인 불균형 구조와 점수 순위 문제가 두 AUC의 큰 차이에 함께 작용할 수 있지만, 원시 200개 예측값과 신뢰구간이 공개되지 않아 원인을 정확히 나눌 수는 없다.
정상 장기외출은 특히 중요한 경계다. 평가 코드에서 이 시나리오는 24시간 사용량을 0으로 만드는 no_usage_24h와 같은 관측 조작을 쓰고 기대 라벨만 NORMAL_PATTERN으로 둔다. 수도 데이터만 본다면 두 상황을 가를 정보가 없다. 0%라는 결과는 모델 하나를 더 튜닝하면 반드시 해결되는 단순 버그가 아니라 입력만으로 식별할 수 없는 문제를 드러낸다.
average_detection_delay_hours=0이라는 기록도 실제로 이상이 시작된 즉시 탐지했다는 뜻이 아니다. 구현은 주입 종료시점에서 한 번 점수화한다. 이 값을 실시간 탐지 지연 0시간으로 홍보하면 평가 범위를 넘어선다.
JAGYEOKSU에서 배우는 ML적 사고력
ML적 사고력은 최신 알고리즘을 고르는 능력보다 업무 질문을 관측 가능한 문제로 바꾸고, 틀렸을 때의 비용까지 포함해 검증하는 능력에 가깝다. JAGYEOKSU를 이 관점으로 보면 Isolation Forest 자체보다 문제 정의, 특징 표현, 평가 설계와 운영 안전장치가 먼저 보인다.
모델보다 의사결정 단위를 먼저 정한다
먼저 “Isolation Forest를 쓸까?”가 아니라 무엇을, 언제, 어떤 행동을 위해 판단할지 정해야 한다. JAGYEOKSU의 한 표본은 사람이 아니라 특정 평가시각의 계량기 이력을 23개 특징으로 바꾼 계량기-평가시각 단위다. 출력도 건강 상태 확정이 아니라 NORMAL, OBSERVE, CHECK_REQUIRED, DATA_ERROR 가운데 하나로 검토 순서를 만든다.
따라서 문제 정의는 “고독사를 예측한다”가 아니라 “평소와 다른 수도 사용 패턴 중 사람이 확인할 후보를 감당 가능한 수로 정렬한다”에 가깝다. 후속 행동이 전화 확인인지 자동 방문인지에 따라 감수할 오탐과 미탐 비용이 달라지고, 그에 맞춰 라벨·임계치·평가 지표도 바뀐다.
관측 신호와 실제 정답도 분리해야 한다.
| 구분 | JAGYEOKSU에서의 예 | 말할 수 있는 범위 |
|---|---|---|
| 관측 신호 | 시간별 수도 사용량, 결측 표시, 23개 파생 특징 | 사용 패턴이 평소와 다른지 |
| 모델 출력 | 고립 트리 원점수, 0~100 ML 점수, 혼합 최종점수 | 특징 공간의 이례성과 검토 우선순위 |
| 공개 평가 라벨 | 무사용·급감·누락 등 합성 주입 시나리오 | 주입한 수도 패턴을 탐지했는지 |
| 실제 필요한 결과 라벨 | 확인 후 외출·입원·고장·누수·도움 필요 여부 | 현장 의사결정이 적절했는지 |
합성 주입 라벨은 실제 결과를 대신하는 proxy label이다. 개발 초기의 회귀 테스트에는 유용하지만 “48시간 무사용을 잡았다”를 “건강 위험을 잡았다”로 바꿔 말할 수는 없다. 공개 Precision과 Recall도 실제 건강 상태가 아니라 주입한 수도 패턴을 기준으로 계산된 값이다.
특징은 데이터가 아니라 업무 가설의 표현이다
일반 IsolationForest는 시간 순서나 생활 의미를 자동으로 이해하지 않는다. 같은 0이라도 새벽의 정상 무사용, 24시간 연속 무사용, 통신 누락은 뜻이 다르다. 최근 사용량, 마지막 사용 이후 시간, 개인 7·30일 기준선, 같은 시간대 편차와 결측률을 만드는 일은 원시 데이터를 모델이 구분할 수 있는 가설로 바꾸는 과정이다.
이때 “특징을 많이 만들었는가”보다 다음을 물어야 한다. 개인 기준선이 집단 평균보다 실제 생활 변화를 잘 표현하는가, 같은 정보를 반복한 상관 특징이 신호를 흐리지 않는가, 결측을 0으로 채우며 만든 연속 무사용과 실제 무사용을 후속 단계에서 분리하는가가 더 중요하다. 23개 특징은 합리적인 출발점이지만 최적이라는 증거는 아니다. RobustScaler 유무와 특징 그룹 제거 전후를 같은 조건에서 비교한 ablation 결과도 현재 공개돼 있지 않다.
시계열 평가에서는 무작위 행 분할을 피해야 한다. 한 계량기의 인접 시점이나 미래 생활 패턴이 학습과 검증에 함께 들어가면 성능이 부풀 수 있다. JAGYEOKSU가 앞선 67%를 학습에 쓰고 평가시각 이후 데이터를 특징에서 제외한 이유다. 다만 한 번의 시간 분리만으로 계절 변화, 계량기 교체, 다른 지역과 가구에 대한 일반화까지 증명되지는 않는다. 개인 기준선, scaler, q01/q99, 임계치 역시 학습 구간 안에서만 맞추고, 이후에는 여러 시점의 forward split과 외부 데이터로 확인해야 한다.
좋은 지표 하나보다 오류의 종류와 비용을 본다
JAGYEOKSU의 ROC-AUC 0.300과 PR-AUC 0.875는 ML 지표를 맥락 없이 읽으면 안 되는 사례다. 공개 평가 시나리오 10개 중 9개가 양성이고 정상 장기외출 하나만 음성이므로 양성 비율이 90%다. 이 조건에서는 무정보 기준의 기대 precision도 약 0.9이므로 PR-AUC 0.875를 숫자만 보고 높은 성능이라고 해석하기 어렵다. 반면 ROC-AUC 0.300은 공개된 점수와 라벨 정의대로라면 양성과 음성의 순위 분리가 좋지 않았다는 경고다.
특정 임계치에서의 Precision 0.862·Recall 0.728·F1 0.781과 전체 임계치의 AUC가 동시에 엇갈릴 수 있는 이유는 서로 다른 질문에 답하기 때문이다. 운영 판단에는 confusion matrix의 TP·FP·FN·TN 절대 건수, 시나리오별 탐지율, 하루 예상 경보 수와 담당자 처리 용량이 함께 필요하다. 다만 원시 200개 예측값과 곡선 계산 방식, 신뢰구간이 공개되지 않아 이 불일치의 원인을 현재 자료만으로 확정할 수는 없다.
정상 장기외출 구분 0%는 더 근본적인 문제를 드러낸다. 평가 코드에서 정상 장기외출과 24시간 위험 무사용은 같은 수도 관측을 만들고 기대 라벨만 다르다. 입력이 같다면 어떤 모델도 “외출 중”이라는 원인을 안정적으로 알아낼 수 없다. 이는 모델 튜닝보다 먼저 확인해야 할 식별 불가능성이다. 해결책은 트리를 더 늘리는 것이 아니라, 적법하고 최소한의 외출 맥락을 추가하거나 출력을 “위험 확정”이 아닌 “24시간 무사용 확인 필요”로 제한하는 것이다.
baseline·ablation·임계치·drift가 운영 성능을 만든다
혼합 점수가 실제로 가치 있는지는 24시간 무사용이면 확인 같은 단순 규칙 baseline과 비교해야 알 수 있다. 같은 시간 홀드아웃에서 ML만, 통계만, 규칙만, 특징 그룹 제외, scaler 제외, DATA_ERROR 분기 제외를 차례로 비교해야 각 구성요소의 기여를 확인할 수 있다. 현재 공개 자료만으로는 0.45/0.35/0.20 혼합이 각 단독 구성보다 낫다고 단정할 수 없다.
임계치도 고정된 진리가 아니다. ML 50과 최종 45·70은 오탐 전화 비용, 미탐 위험, 하루 처리 가능한 확인 건수로 보정하고 별도 미래 구간에서 다시 평가해야 한다. 배포 후에는 입력 특징, 결측률, 점수 분포, 상태별 경보율과 사람 확인 결과의 drift를 추적해 재학습·재보정·롤백 조건을 정해야 한다.
데이터 오류와 모델 오류를 분리하는 것도 같은 맥락이다. 센서 미수신을 무사용으로 처리해 높은 점수가 나왔다면 모델이 위험을 발견한 것이 아니라 데이터 파이프라인이 실패한 것이다. JAGYEOKSU가 개인 결측률이나 여러 계량기의 동시 누락이 높을 때 DATA_ERROR를 점수보다 우선하는 이유다. 운영 로그에서도 데이터가 없어 판단하지 못한 건, 유효한 데이터에서 발생한 FP/FN, 규칙이나 혼합 임계치로 상태가 바뀐 건을 따로 집계해야 재수집·특징 수정·재학습·임계치 조정 중 맞는 처방을 고를 수 있다.
마지막으로 human-in-the-loop는 면책 문구가 아니라 시스템의 일부다. 담당자에게 점수뿐 아니라 무사용 시간, 결측 여부, 개인 기준선 대비 변화 같은 근거를 제공하고, 확인 결과를 외출·누수·고장·도움 필요·미확인처럼 구조화해 다음 평가 데이터로 돌려보내야 한다. 모델 성능도 AUC만이 아니라 정해진 인력으로 중요한 후보를 얼마나 확인했는지까지 포함해 측정해야 한다.
내 ML 프로젝트에 적용할 9가지 질문
- [ ] 모델을 고르기 전에 의사결정 대상, 판단 시점과 후속 행동을 한 문장으로 정의했는가?
- [ ] 관측 신호, 모델 출력, proxy label과 실제 결과 라벨을 구분했는가?
- [ ] 원시 데이터를 어떤 업무 가설로 특징화했으며 개인·집단 기준선 선택 근거가 있는가?
- [ ] 데이터 split이 실제 배포의 시간 흐름을 따르고 미래 누수를 차단하는가?
- [ ] confusion matrix 절대 건수, Precision/Recall, ROC/PR, 양성 비율과 시나리오별 실패를 함께 보는가?
- [ ] 서로 다른 원인이 같은 관측을 만들 때 애초에 식별 가능한 문제인지 확인했는가?
- [ ] 단순 baseline보다 나으며, 특징·모델·규칙별 ablation 근거가 있는가?
- [ ] 임계치를 오류 비용과 처리 용량으로 정하고 입력·점수·경보율의 drift를 감시하는가?
- [ ] data error와 model error를 분리하고 사람의 확인 결과가 다음 평가 라벨로 환류되는가?
코드는 읽었지만 모델 동작을 재현했다고 말할 수 없는 이유
조사 환경은 macOS와 pyenv Python 3.12.9였고, 프로젝트의 pyproject.toml은 Python 3.12 이상과 scikit-learn 1.5 이상 2 미만을 요구했다. 관련 소스와 테스트를 대조하고 다음 문법 컴파일은 통과했다.
$ pyenv exec python -m compileall -q app scripts
compileall: PASS
[exit 0]
하지만 핵심 단위 테스트 실행은 애플리케이션 로직에 도달하기 전에 멈췄다.
$ pyenv exec python -m pytest tests/unit/test_water_anomaly_model.py tests/unit/test_anomaly_analysis_service.py -q
/Users/sungpyo/.pyenv/versions/3.12.9/bin/python: No module named pytest
[exit 1]
이는 JAGYEOKSU 테스트 실패가 아니라 조사 환경에 개발 의존성이 설치되지 않은 상태다. 동시에 문법 컴파일 성공은 import, 학습, 추론, 점수 결과가 맞다는 증거도 아니다. test_fit_predict_and_scores, test_random_state_reproducibility, test_missing_data_takes_precedence, test_no_future_data_leakage 등의 입력과 기대값은 구현과 대조했지만 실행 결과로 바꿔 말하지 않는다.
실제 배포 전에는 프로젝트가 허용하는 scikit-learn 버전을 모델 metadata에 고정해 기록하고, 원본 데이터와 artifact를 확보한 격리 환경에서 테스트와 공개 평가를 다시 실행해야 한다. 지역·계절·계량기·가구 특성이 다른 외부 데이터에서도 임계치와 오탐을 재검증해야 한다.
운영에서는 점수보다 확인 절차가 먼저다
이 시스템이 유용할 수 있는 자리는 자동 판정이 아니라 한정된 확인 인력을 위한 큐다. DATA_ERROR를 먼저 분리하고, CHECK_REQUIRED 후보에 왜 점수가 올랐는지 근거를 붙여 담당자가 계량기 상태와 최소한의 맥락을 확인하는 방식이다.
다음 조건이라면 도입 후보로 추천할 수 있다.
- 확정 이상 라벨은 부족하지만 수치형 로그·센서 특징은 충분하다.
- 이상이 정상보다 드물고 특징 공간에서도 어느 정도 다르다는 업무 근거가 있다.
- 목표가 사건의 자동 확정이 아니라 검토할 후보의 우선순위화다.
- 시계열을 무사용 길이, 최근 합계, 개인 기준선, 주기 편차처럼 의미 있는 특징으로 바꿀 수 있다.
- 시간 홀드아웃과 사람 검토 표본으로 오탐·미탐을 측정하고, drift를 감시할 수 있다.
contamination과 후속 임계치를 검토 인력과 오탐 비용에 맞춰 조정할 수 있다.
반대로 다음 조건에서는 단독 사용을 피해야 한다.
- 높은 점수만으로 건강·생존·누수·고장 중 원인을 식별해야 한다.
- 정상 장기외출과 위험한 무사용처럼 관측값이 같은 사건을 자동 구분해야 한다.
- 시간 순서를 지킨 평가나 정상 검토 표본을 준비할 수 없다.
- 계절·가구·센서 변화가 큰데 점수 분포와 경보율 drift를 감시하거나 재학습할 수 없다.
- 범주형 원본을 임의 정수로 바꿨거나 고차원 one-hot·상관 특징의 영향을 검증하지 않았다.
- 경보를 즉시 연락, 방문, 불이익 또는 행정처분으로 연결하려 한다.
어떤 경우든 이상점수만으로 연락, 방문, 불이익 또는 행정처분을 자동 실행해서는 안 된다. 시간별 생활 패턴은 계량기 ID가 익명이어도 민감할 수 있다. 최소 수집, 접근 통제, 보존 기간, 삭제, 감사 로그, 이의 제기와 사람의 재검토 절차가 필요하다. 공개 평가의 라벨 역시 실제 건강 상태가 아니라 합성 수도 이상 시나리오다. 이를 고독사나 응급상태 예측 성능으로 바꾸어 표현할 근거가 없다.
JAGYEOKSU의 설계에서 가져갈 부분은 고립 트리의 점수 자체보다 경계를 나눈 방식이다. 미래 데이터 누수를 막고, 개인 기준선과 명시적 규칙을 병행하며, 0과 결측을 분리하고, 실패 시나리오를 공개한 뒤 최종 행동을 사람에게 남긴다. 시계열 이상 탐지 시스템을 도입한다면 먼저 모델을 고르기보다 “무엇을 관측할 수 없으며, 오탐을 누가 어떻게 확인할 것인가”부터 설계하는 편이 맞다.
JAGYEOKSU를 더 살펴보려면 저장소 README에서 전체 흐름을 확인한 뒤 FeatureGenerator, WaterAnomalyModel, AnomalyAnalysisService 순서로 읽는 것이 좋다. 모델 출력의 제한은 모델 카드에 정리돼 있다.