가장 무거운 지표를 뺀 이유: value_accuracy 제거와 DSC 엔진 v4 (ADR-009)

안녕하세요, 고준서입니다. 캡스톤 팀에서 데이터 품질 점수 엔진(DSC)과 실험을 맡았습니다. 4월 25일에 이상치 지표의 자기참조 문제를 고치면서 value_accuracy라는 지표를 새로 넣었고, 그 덕에 점수와 모델 성능의 상관 r이 0.08에서 0.42로 올라왔습니다. 그런데 이틀 뒤 그 지표를 뺐습니다. 원본과의 거리를 재는 지표라 원본 없이는 계산이 안 됐고, 그러면 “데이터 품질 점수”라는 이름과 모순이었거든요. 이 글에는 왜 뺐는지, 대신 뭘 넣었는지, 그리고 결과를 보기 전에 적어 둔 합격 조건을 실제 결과가 넘었는지를 ADR과 커밋 번호를 붙여 적었습니다.

잘 되는 지표를 왜 빼냐면

value_accuracy는 원본 데이터(reference)의 분포와 지금 데이터의 분포가 얼마나 다른지를 잽니다. 수치형은 KS 통계량, 범주형은 총변동거리요. 가중치 0.30으로 8개 지표 중 압도적으로 컸습니다.

문제는 정의에 있었습니다. 원본과의 거리니까 원본 자체에서는 항상 1.0입니다. 어떤 데이터든 “이게 원본이다”라고 주면 만점을 받아요. 그러면 이건 데이터가 얼마나 좋은지를 재는 게 아니라 원본에서 얼마나 멀어졌는지를 재는 drift 도구입니다. ADR-009에 이렇게 적혀 있습니다.

reference 의존이라 baseline 자체에서는 1.0 (자명) → 절대 품질 점수가 아닌 drift 측정 도구. 정의(원본 = 100점)가 임의 anchor에 갇혀, “데이터 품질 점수”라는 제품 정체성과 모순

DSC는 반드시 “데이터 품질 점수”여야 한다는 건 제가 4월 27일에 못 박은 요구사항이었고, ADR에도 그렇게 기록돼 있습니다. 상관계수를 올리려고 정의를 무너뜨리면 캡스톤 질문 자체가 바뀌어 버립니다. “품질 점수가 성능을 예측하는가”가 “원본에서 멀어진 정도가 성능을 예측하는가”가 되는 거죠. 후자는 거의 당연한 얘기라 증명할 가치가 없습니다.

대신 넣은 두 지표

원본 없이 데이터 하나만 보고 계산할 수 있으면서, 모델의 학습 가능성과 실제로 연결되는 지표를 찾았습니다.

label_consistency(가중치 0.20)는 각 샘플의 k개 최근접 이웃 중 자기와 라벨이 같은 비율의 평균입니다. 라벨에 노이즈가 많거나 클래스가 잘 안 갈리면 이웃 라벨이 자기와 달라지고 점수가 내려갑니다. feature_informativeness(0.10)는 각 피처가 라벨에 주는 상호정보량(mutual information)의 평균입니다. 피처가 라벨과 무관하면 0에 가깝고, 결측이나 노이즈가 들어가면 정보가 줄어듭니다.

지표 v3.2 v4.0
completeness 0.20 0.20
uniqueness 0.15 0.15
validity 0.05 0.05
consistency 0.10 0.10
outlier_ratio 0.05 0.05
class_balance 0.10 0.10
feature_correlation 0.05 0.05
value_accuracy 0.30 제거
label_consistency   0.20
feature_informativeness   0.10

표 1. 가중치 변경(ADR-009). value_accuracy의 0.30을 둘로 나눠 줬고 나머지는 손대지 않았습니다.

이 둘 말고 다른 후보를 비교한 기록은 없습니다. ADR-009에는 두 지표의 근거(“ML 인과” 절)와 위험만 적혀 있고, 무엇을 놓고 골랐는지는 남아 있지 않아요. 지금 되묻는다면 “다른 후보가 없었다”가 아니라 “적어 두지 않았다”가 정확한 답입니다.

reference_df 인자는 outlier_ratio(IQR 기준 고정)와 consistency(새 표현 비율)에만 보조로 남았습니다. 점수의 30%를 차지하던 의존성이 사라졌으니 원본이 없는 데이터에서도 지표 7개는 그대로 돕니다.

이 결정에는 스스로 적어 둔 위험이 하나 있습니다. label_consistency는 분류 가능성을 거의 직접 재는 지표라, F1과 상관이 높게 나오면 “품질 점수가 성능을 예측한다”가 동어반복처럼 들릴 수 있습니다. 그래서 오염 종류 하나를 빼고 가중치를 맞춘 뒤 뺀 종류에서도 상관이 나오는지 보는 hold-out 검증을 같이 걸었습니다.

결과를 보기 전에 합격 조건을 적었습니다

ADR-009의 상태는 “Phase 1 시작 전 사전 등록”입니다. 노트북을 돌리기 전에 이 네 가지를 통과해야 v4를 채택한다고 적었습니다.

  • baseline에서 두 신지표 값 ≥ 0.7 (정상 데이터)
  • feature_accuracy 0.75 폴루션 → label_consistency Δ ≤ −0.10
  • completeness 0.75 폴루션 → 두 지표 모두 적절히 하락
  • uniqueness 0.75 폴루션 → 두 지표 거의 변화 없음 (false positive 검증)

결과를 보고 나서 조건을 정하면 어떤 결과든 맞출 수 있으니까요. 네 가지는 notebooks/_dev/verify_v4_metrics.py로 실제 오염 데이터에서 확인했고 4/4 통과였습니다. 그다음 기존 오염 데이터에 v4를 적용한 시뮬레이션을 먼저 돌리고, Colab에서 노트북 01, 02, 04를 정식으로 다시 돌렸습니다.

지표 v3.2 정식 v4 정식
Pearson r 0.420 0.598
Spearman ρ 0.365 0.628
ANOVA F 32.9 84.4
비선형 R² (RF 5-fold)   0.632 ± 0.091
등급별 F1 평균 A 0.72, B 0.65, C 0.60, D 0.47 A 0.88, B 0.78, C 0.60, D 0.52
데이터셋별 유의 2/3 3/3
hold-out 4/5 5/5

표 2. v3.2에서 v4로(진행 기록 20260427-01, 커밋 ba05f96). n=435, 오염 설정 87개 × 모델 5개.

시뮬레이션이 예측한 r과 정식 r의 차이는 0.013이었습니다. hold-out에서 v3.2 때 떨어졌던 uniqueness가 0.224에서 0.747로 올라와 5/5가 됐고요.

데이터셋 3개 중 Telco만 r이 0.287로 약했습니다. 원인을 세 가설로 나눠 봤는데(ADR-010), TotalCharges를 0으로 채운 부작용도 아니었고 F1 macro가 둔한 것도 아니었습니다. 범주형 열 17개를 단순 원핫 인코딩하면 13,615차원이 되는 게 원인이었어요. 남독일 신용은 54차원, letter는 0차원인데요. 선형과 트리 모델은 이 희소 공간에서 r이 0.09에서 0.27에 그쳤고 MLP만 0.61이었습니다. target encoding이나 차원 축소를 넣으면 나아지겠지만 캡스톤 범위를 넘는다고 보고, DSC의 결함이 아니라 ML 설정의 한계로 README에 명시하고 넘어갔습니다.

재실행 중 사고도 하나 있었습니다. 노트북 01의 18번 셀이 model_performance.csv를 기준선 15행으로 덮어써 버려서 노트북 04의 7번 셀에서 ValueError가 났습니다. 시뮬레이션 때 _dev/simulated_v4_merged.csv에 v3.2 학습 결과 435건을 보존해 둔 게 있어 거기서 복원했고, 18번 셀은 기준선 행만 갱신하는 upsert로 바꿨습니다. 노트북 03을 다시 돌려 복구했으면 개선 보고서 추정으로 100분에서 120분이 드는 일이었습니다.

이날 작업은 Claude Opus 4.7과 함께했고 커밋에 공동 작성자로 남아 있습니다. 신지표 구현과 검증 스크립트, 시뮬레이션, 노트북 04의 분석 셀 14개가 그쪽 몫이었고, “품질 점수여야 한다”는 제약을 정한 건 저고, value_accuracy 제거는 그 제약에서 따라 나온 결정입니다.

남은 질문

label_consistency가 성능을 너무 직접 재는 것 아니냐는 위험은 hold-out 5/5로 일부 답했지만, 완전히 지워지지는 않습니다. kNN 라벨 일관성이 높은 데이터는 kNN이 잘 되는 데이터고, 그런 데이터는 다른 모델도 대체로 잘 됩니다. “품질”과 “학습 가능성” 사이 어디에 선을 긋느냐는 이 캡스톤이 끝까지 안고 간 질문입니다.



함께 읽을 만한 글