평가 데이터 누수: 행 분할과 원본·그룹·시간 분할의 차이

이 카테고리 글 목록

전체글 보기

평가용 행을 따로 빼 두었어도 같은 원본 문서의 다른 페이지가 학습 쪽에 남아 있으면 ‘처음 보는 문서에 잘 맞는가’를 시험하기 어렵다. 행이 겹치지 않는지와 원본이 겹치지 않는지는 다른 검사다. 먼저 평가하려는 대상이 새 페이지인지, 새 문서인지, 새 사용자나 미래 요청인지 정해야 한다.

아래 A·B·C 문서와 페이지 여섯 개는 설명용 합성 자료다. 실제 문서·개인정보·모델 실행 결과를 사용하지 않았다. 코드는 분할 사이의 원본 ID 교집합을 확인할 뿐, 학습이나 성능 점수를 계산하지 않는다.

행 단위로 나누면 원본 세 개가 모두 양쪽에 남는다

각 원본에서 두 페이지를 추출한 합성 입력
원본 ID 페이지 행 행별 분할 예
A A1, A2 A1은 학습, A2는 평가
B B1, B2 B1은 학습, B2는 평가
C C1, C2 C1은 학습, C2는 평가

학습 행 A1·B1·C1과 평가 행 A2·B2·C2는 서로 다른 행이다. 그러나 두 묶음의 원본 ID 집합은 모두 {A, B, C}다. 같은 문서의 서식·문구·스캔 흔적 등이 양쪽에 공유될 수 있다. 원본 ID 열을 모델 입력에서 빼도 문서 내용에 남은 특징까지 사라지는 것은 아니다.

배포 목표가 새 원본 문서 처리라면, 이 분할의 좋은 점수에는 이미 본 원본과의 유사성이 섞일 수 있다. 반대로 실제 사용이 이미 등록된 문서의 추가 페이지 처리라면 그 목표에 맞춘 별도 평가가 가능하다. 같은 원본의 존재만으로 모든 평가가 무효라고 선언하기보다, 무엇에 일반화하려는지를 먼저 밝힌다.

새 원본을 평가하려면 원본을 통째로 나눈다

학습에 A1·A2·B1·B2를 두고 평가에 C1·C2를 두면 원본 ID 교집합은 비어 있다. scikit-learn의 그룹 교차검증 안내도 보지 못한 그룹에 대한 성능을 평가할 때 학습과 검증 사이에 같은 그룹이 남지 않도록 설명한다. GroupKFold는 이런 그룹 분리를 위한 도구다.

무엇을 그룹으로 잡을지는 업무에 따라 다르다. 같은 원본의 페이지·잘라낸 이미지·OCR 재처리본·번역이나 요약 파생본은 원본 계보를 따라 묶을 수 있다. 새 사용자에 대한 일반화가 목적이라면 사용자 단위도 살펴야 한다. 문서 ID만 다르고 같은 사용자의 특징을 공유하면 문서 분리만으로 새 사용자 평가가 되지는 않는다.

다음 예에서 C 하나를 남긴 것은 교집합 검산을 위한 작은 사례다. 평가 원본이 한 개뿐이므로 다양한 새 문서를 대표하는 충분한 성능 평가가 아니다. 실제 설계에서는 그룹 수, 그룹별 크기와 클래스 구성도 따로 확인해야 한다.

본문 코드로 행 교집합과 원본 교집합을 구별한다

아래 JavaScript를 Windows의 Node.js v24.15.0에서 본문 HTML의 코드 텍스트를 추출·entity 해제해 실행했다. 외부 패키지와 서비스 호출은 없다.

const rows = [
  {id: "A1", original: "A"}, {id: "A2", original: "A"},
  {id: "B1", original: "B"}, {id: "B2", original: "B"},
  {id: "C1", original: "C"}, {id: "C2", original: "C"}
];
const intersection = (a, b) => {
  const right = new Set(b);
  return [...new Set(a)].filter(x => right.has(x)).sort();
};
function inspect(name, train, test) {
  return {
    name, trainRows: train.length, testRows: test.length,
    rowOverlap: intersection(train.map(x => x.id), test.map(x => x.id)),
    originalOverlap: intersection(
      train.map(x => x.original), test.map(x => x.original))
  };
}
const rowTrain = rows.filter(x => x.id.endsWith("1"));
const rowTest = rows.filter(x => x.id.endsWith("2"));
const groupTrain = rows.filter(x => x.original !== "C");
const groupTest = rows.filter(x => x.original === "C");
console.log(JSON.stringify(inspect("rowSplit", rowTrain, rowTest)));
console.log(JSON.stringify(inspect("groupSplit", groupTrain, groupTest)));
{"name":"rowSplit","trainRows":3,"testRows":3,"rowOverlap":[],"originalOverlap":["A","B","C"]}
{"name":"groupSplit","trainRows":4,"testRows":2,"rowOverlap":[],"originalOverlap":[]}

두 분할 모두 같은 행 ID는 겹치지 않는다. 차이는 원본 ID다. 이 검사는 주어진 계보가 정확하다는 조건에서만 작동한다. 원본 ID가 누락됐거나 파생본에 새 ID만 붙었다면 내용 중복·근접 중복을 별도로 조사해야 한다. 빈 교집합이 모든 누수의 부재를 입증하지는 않는다.

미래 요청이 목적이면 시간 경계도 필요하다

그룹을 분리해도 미래 자료가 과거 예측의 학습에 들어갈 수 있다. 미래 요청 처리가 목적이면 학습 자료와 사용 가능한 특징을 기준 시점 이전으로 제한하고 이후 기간을 평가하는 설계를 검토한다. 라벨이 늦게 확정되거나 처리 이력이 뒤늦게 붙는 경우에는 행의 생성 시각뿐 아니라 그 정보를 실제로 알 수 있었던 시각도 살펴야 한다.

공식 시간계열 교차검증 안내는 시간 상관이 있는 자료의 무작위 분할에 주의를 주고 미래 관측에 대한 평가를 설명한다. 다만 TimeSeriesSplit 하나가 그룹 분리까지 자동 보장하는 것은 아니다. 새 사용자이면서 미래 기간이라는 목표라면 두 조건을 함께 만족하는 분할을 설계하고 교집합·시각을 확인해야 한다.

분할 뒤에도 전처리와 수정 과정에서 누수가 생긴다

scikit-learn의 데이터 누수 안내는 평가 자료로 fit하지 않고 변환 규칙을 학습 자료에서만 배우도록 설명한다. 예를 들어 수치 정규화의 평균을 전체 자료에서 계산하면 평가 묶음의 정보가 학습 단계에 들어간다. 학습 쪽에서 구한 평균·결측 대체값·어휘 선택 등을 고정하고 평가 쪽에는 같은 변환을 적용해야 한다.

교차검증에서는 각 fold의 학습 부분 안에서 전처리도 다시 학습해야 한다. 적절히 구성한 Pipeline이 이를 돕지만, 이미 전체 자료로 특징을 골랐거나 잘못 만든 그룹을 넣었다면 Pipeline이라는 이름만으로 해결되지 않는다. 여기서는 scikit-learn 학습이나 Pipeline을 실행하지 않았으므로 특정 라이브러리 설정의 성능을 검증했다고 볼 수 없다.

프롬프트 평가에도 비슷한 경계가 있다. 최종 평가 답을 계속 들여다보며 프롬프트·임계값을 고쳤다면 그 묶음은 개발 과정에 쓰인 자료다. 최종 확인용으로 남겨 둔 새 자료와 구별한다. 프롬프트 회귀 평가의 입력 분리 원칙을 실제 적용할 때 행 ID 외에 원본·사용자·시간·파생 관계를 함께 기록하면 분할의 의미를 점검할 수 있다.

자주 묻는 질문

무작위 seed를 고정하면 누수를 막을 수 있나요?

seed 고정은 같은 무작위 분할을 재현하는 데 도움이 된다. 같은 원본을 양쪽에 넣는 분할이라면 그 문제도 그대로 재현하므로, 그룹과 시간 조건을 별도로 확인해야 한다.

그룹 분리 후 점수가 낮아지면 모델이 나빠진 건가요?

같은 모델이라도 평가 질문과 난도가 바뀐 결과일 수 있다. 새 원본 평가와 이미 본 원본의 추가 행 평가를 같은 조건의 전후 개선 수치처럼 비교하지 않는다.

그룹 ID가 다르면 내용 중복 검사는 필요 없나요?

필요할 수 있다. 다른 이름으로 저장한 사본이나 여러 원본을 합친 파생물은 ID 교집합만으로 찾기 어렵다. 표의 행과 키가 뜻하는 단위를 확인하는 방법처럼, 행 수보다 한 행·한 ID가 실제 무엇을 가리키는지 먼저 확인한다.

이 카테고리의 다른 글

전체글 보기

“평가 데이터 누수: 행 분할과 원본·그룹·시간 분할의 차이”에 대한 1개의 생각

댓글 남기기