Skip to main content
Summary

시험 문제를 미리 본 모델은 믿을 수 없다

지난 장에서 우리는 과적합을 배웠다. 모델이 학습 데이터에 너무 딱 맞게 적응하면, 처음 보는 데이터에서는 오히려 약해질 수 있다는 이야기였다. 마치 시험 범위 전체를 이해한 것이 아니라, 기출문제의 정답 위치만 외운 학생과 비슷하다.

그렇다면 우리는 어떻게 알 수 있을까? 이 모델이 정말 세상을 이해한 것인지, 아니면 이미 본 데이터만 그럴듯하게 외운 것인지 말이다.

여기서 등장하는 가장 기본적인 장치가 학습 데이터와 테스트 데이터의 분리다.

머신러닝에서 데이터는 한 덩어리로 보이지만, 실제 모델을 만들 때는 역할을 나누어야 한다. 일부 데이터는 모델이 공부하는 데 쓰고, 일부 데이터는 끝까지 숨겨 두었다가 마지막 시험에 사용한다. 모델 입장에서 테스트 데이터는 한 번도 본 적 없는 새 문제다. 그래서 테스트 데이터에서의 성능은 모델이 실전에서 어느 정도 해낼지를 추정하는 중요한 단서가 된다.

핵심은 단순하다.

모델에게 모든 문제를 보여 주고 나서 "얼마나 잘하니?"라고 묻는 것은 공정한 평가가 아니다. 모델이 보지 못한 문제를 따로 남겨 두어야 한다.

학습 데이터와 테스트 데이터

학습 데이터는 공부용, 테스트 데이터는 실전 모의고사

머신러닝 프로젝트에서 가장 흔한 흐름은 다음과 같다.

먼저 전체 데이터를 준비한다. 그다음 데이터를 두 부분으로 나눈다. 하나는 학습 데이터, 다른 하나는 테스트 데이터다. 모델은 학습 데이터만 보고 규칙을 배운다. 집값 예측 모델이라면 면적, 방 개수, 위치 같은 특징과 실제 집값 사이의 관계를 학습한다. 스팸 메일 분류기라면 단어, 발신자, 링크 개수 같은 특징과 스팸 여부 사이의 관계를 학습한다.

학습이 끝나면 모델에게 테스트 데이터를 보여 준다. 이때 테스트 데이터의 정답은 평가할 때만 사용한다. 모델은 테스트 데이터의 입력을 보고 예측을 내놓고, 우리는 그 예측을 실제 정답과 비교한다. 여기서 얻은 점수가 모델의 실전 성능을 가늠하는 기준이 된다.

이 흐름을 이전에 배운 머신러닝의 기본 구조에 연결해 보면 다음과 같다.

데이터를 준비한다 → 학습용과 테스트용으로 나눈다 → 학습 데이터로 특징과 라벨의 관계를 배운다 → 테스트 데이터로 처음 보는 상황에서 예측한다 → 실제값과 비교한다 → 실전 성능을 추정한다.

여기서 중요한 점은 모델이 테스트 데이터로는 학습하지 않는다는 것이다. 테스트 데이터는 모델을 고치는 데 쓰는 재료가 아니라, 모델이 얼마나 잘 만들어졌는지 확인하는 거울이다.

같은 데이터로 학습하고 평가하면 무슨 일이 생길까

어떤 사람이 영어 단어 시험을 본다고 해 보자. 선생님이 시험지를 미리 나눠 주고, 학생이 그 시험지의 정답만 외웠다. 다음 날 똑같은 시험지를 다시 풀게 했더니 100점을 받았다. 이 학생은 영어를 잘하는 것일까?

그럴 수도 있지만, 확신하기는 어렵다. 영어 실력이 늘었다기보다 그 시험지에만 익숙해졌을 가능성이 크다.

머신러닝도 같다. 학습에 사용한 데이터로 다시 평가하면 모델의 점수는 대체로 좋게 나온다. 왜냐하면 모델은 이미 그 데이터의 패턴을 보았기 때문이다. 특히 모델이 복잡할수록 학습 데이터의 세세한 흔들림까지 따라갈 수 있다. 지난 장에서 본 과적합이 바로 이 지점에서 문제가 된다.

예를 들어 집값 예측 모델을 만들었다고 하자. 학습 데이터 안에는 어떤 집이 우연히 비싸게 팔린 사례가 있을 수 있다. 또 어떤 지역은 실제 가치보다 일시적으로 가격이 높게 기록되었을 수도 있다. 모델이 이런 우연한 사례까지 규칙처럼 외워 버리면, 학습 데이터에서는 오차가 작아진다. 그러나 새로운 매물에는 그런 우연이 똑같이 반복되지 않는다. 실전에서는 예측이 흔들린다.

분류 문제에서도 비슷하다. 스팸 메일 분류기가 학습 데이터에 있던 특정 문구를 지나치게 믿는다고 해 보자. "무료 당첨"이라는 표현이 들어가면 거의 스팸이라고 배웠는데, 실제로는 정상적인 이벤트 안내 메일에도 그 표현이 들어갈 수 있다. 학습 데이터에서는 잘 맞았지만 새 메일에서는 틀릴 수 있다.

그래서 같은 데이터로 학습과 평가를 모두 하면 성능을 과대평가하게 된다. 모델이 정말 잘하는 것이 아니라, 이미 본 문제에 익숙해진 것일 수 있기 때문이다.

훈련 성능과 테스트 성능을 함께 봐야 한다

모델을 평가할 때는 보통 두 가지 성능을 비교한다. 하나는 학습 데이터에서의 성능이고, 다른 하나는 테스트 데이터에서의 성능이다.

학습 데이터에서의 성능을 흔히 훈련 성능이라고 부른다. 모델이 공부한 문제를 얼마나 잘 맞히는지 보여 준다. 테스트 데이터에서의 성능은 모델이 처음 보는 문제를 얼마나 잘 맞히는지 보여 준다.

이 둘을 비교하면 모델의 상태를 어느 정도 짐작할 수 있다.

상황해석
훈련 성능도 낮고 테스트 성능도 낮다모델이 아직 충분히 배우지 못했을 가능성이 크다
훈련 성능은 높지만 테스트 성능은 낮다학습 데이터에 과적합되었을 가능성이 크다
훈련 성능과 테스트 성능이 모두 적절히 높다비교적 일반화가 잘되고 있을 가능성이 있다

여기서 "적절히 높다"라는 말이 조금 애매하게 느껴질 수 있다. 실제로 그렇다. 좋은 성능의 기준은 문제마다 다르다. 집값 예측에서는 평균 오차가 몇백만 원인지 몇천만 원인지가 중요하고, 질병 예측에서는 놓치면 안 되는 환자를 얼마나 잘 찾아내는지가 중요하다. 스팸 분류에서는 정상 메일을 스팸으로 잘못 보내는 일이 치명적일 수 있다.

그래서 성능 숫자는 항상 문제의 맥락과 함께 읽어야 한다. 하지만 어떤 문제든 변하지 않는 원칙이 있다. 모델의 실전 능력을 보려면, 모델이 학습하지 않은 데이터에서 평가해야 한다.

데이터를 어떻게 나눌 것인가

전체 데이터를 학습 데이터와 테스트 데이터로 나누는 방법은 하나만 있는 것이 아니다. 데이터의 성격에 따라 적절한 분할 방식이 달라진다.

가장 기본적인 방법은 무작위 분할이다. 전체 데이터를 랜덤하게 섞은 뒤, 예를 들어 80%는 학습 데이터로, 20%는 테스트 데이터로 사용한다. 많은 입문 예제에서 이 방식을 쓴다. 집값 데이터나 꽃 품종 데이터처럼 각 행이 비교적 독립적인 사례라면 무작위 분할이 잘 맞는 경우가 많다.

하지만 모든 데이터에 무작위 분할을 적용하면 안 된다. 시간의 흐름이 중요한 데이터에서는 특히 조심해야 한다. 내일의 매출을 예측하는 모델을 만든다고 하자. 이때 미래 데이터를 학습에 섞고 과거 데이터를 테스트에 넣으면 이상한 일이 생긴다. 현실에서는 미래를 보고 과거를 예측하지 않는다. 그래서 시계열 데이터에서는 보통 과거 데이터로 학습하고, 그보다 나중 시점의 데이터로 테스트한다. 이것을 시간 순서 분할이라고 생각하면 된다.

또 하나 중요한 경우가 그룹 단위 분할이다. 같은 사용자가 여러 번 등장하거나, 같은 병원의 환자 기록이 여러 개 있거나, 같은 회사의 거래 내역이 반복해서 들어 있는 데이터가 있다. 이때 같은 그룹의 일부는 학습 데이터에, 일부는 테스트 데이터에 들어가면 모델이 사실상 힌트를 얻을 수 있다. 예를 들어 고객 이탈 예측에서 한 고객의 과거 행동 일부가 학습 데이터에 있고, 같은 고객의 다른 기록이 테스트 데이터에 있다면 모델은 완전히 낯선 고객을 예측하는 상황과 다르게 평가된다.

정리하면 데이터 분할 방식은 다음처럼 고르면 된다.

  • 각 데이터가 서로 독립적이고 순서가 중요하지 않다면 무작위 분할을 고려한다.

  • 시간의 흐름에 따라 미래를 예측해야 한다면 시간 순서 분할을 고려한다.

  • 같은 사용자, 같은 상품, 같은 기관처럼 묶음이 중요한 데이터라면 그룹 단위 분할을 고려한다.

데이터를 나누는 일은 단순한 전처리처럼 보이지만, 사실 평가의 공정성을 결정하는 핵심 단계다. 잘못 나누면 모델이 좋아 보이는 착시가 생긴다. 제대로 나누면 모델의 약점이 일찍 드러난다. 머신러닝에서는 약점이 드러나는 것이 나쁜 일이 아니다. 오히려 실전에 나가기 전에 고칠 기회를 얻는 것이다.

무작위 분할: 가장 흔하지만 항상 정답은 아니다

무작위 분할은 직관적이고 편리하다. 전체 데이터를 섞어서 일부를 학습용으로, 일부를 테스트용으로 빼 두면 된다. 예를 들어 1만 개의 데이터가 있다면 8천 개로 모델을 학습시키고, 2천 개로 평가할 수 있다.

이 방식은 데이터가 충분히 섞여 있고, 각 사례가 서로 크게 의존하지 않을 때 효과적이다. 타이타닉 생존 예측처럼 각 승객을 하나의 독립적인 사례로 볼 수 있는 데이터에서는 무작위 분할이 자연스럽다. 물론 같은 가족이나 같은 객실 같은 요소가 있을 수 있지만, 입문 단계에서는 무작위 분할만으로도 학습과 평가의 차이를 이해하기 좋다.

다만 무작위 분할에는 운의 요소가 있다. 우연히 쉬운 데이터가 테스트 쪽에 많이 들어가면 성능이 높게 보일 수 있고, 어려운 데이터가 몰리면 낮게 보일 수 있다. 데이터가 작을수록 이런 흔들림은 커진다. 그래서 다음 장에서 배울 검증 데이터와 교차검증이 필요해진다.

지금은 한 가지 원칙만 기억하면 된다. 무작위 분할은 좋은 출발점이지만, 데이터의 시간, 그룹, 실제 사용 상황을 무시한 채 기계적으로 적용해서는 안 된다.

시간 순서 분할: 미래를 예측한다면 미래를 숨겨라

많은 예측 문제는 시간과 연결되어 있다. 내일의 매출, 다음 달의 고객 이탈, 다음 분기의 재고 수요, 다음 주의 전력 사용량 같은 문제는 모두 과거를 바탕으로 미래를 예측한다.

이런 문제에서 데이터를 랜덤하게 섞으면 미래의 정보가 학습 데이터에 들어갈 수 있다. 모델은 현실에서는 알 수 없는 정보를 은근히 배우게 된다. 겉으로는 테스트 성능이 좋아 보이지만, 실제 배포 후에는 성능이 떨어질 수 있다.

예를 들어 2023년부터 2025년까지의 쇼핑몰 주문 데이터가 있다고 해 보자. 2025년 말의 소비 트렌드가 학습 데이터에 들어가고, 2024년 데이터가 테스트 데이터로 들어간다면 평가는 현실과 반대 방향이 된다. 우리는 2024년에 2025년의 유행을 알 수 없다. 그러므로 실전 상황을 흉내 내려면 과거 구간으로 학습하고, 그 이후 구간으로 테스트해야 한다.

시간 순서 분할은 모델에게 이렇게 말하는 것과 같다.

"여기까지가 네가 지금까지 본 세상이다. 이제 그다음에 벌어진 일을 맞혀 봐."

이 방식은 미래 예측 문제에서 훨씬 현실적인 평가를 제공한다.

그룹 단위 분할: 같은 사람을 양쪽에 넣지 않기

그룹 단위 분할은 처음에는 조금 덜 익숙할 수 있다. 하지만 실전에서는 매우 중요하다.

예를 들어 사용자 행동 데이터를 생각해 보자. 한 명의 사용자가 여러 번 로그인하고, 여러 상품을 보고, 여러 번 구매했을 수 있다. 이 데이터를 행 단위로 랜덤하게 나누면 같은 사용자의 일부 기록은 학습 데이터에 들어가고, 일부 기록은 테스트 데이터에 들어갈 수 있다.

이렇게 되면 모델은 테스트에서 완전히 새로운 사용자를 만나는 것이 아니다. 이미 학습 중에 그 사용자의 행동 일부를 본 상태다. 사람으로 비유하면, 처음 보는 손님의 취향을 맞히는 것이 아니라, 전에 만났던 손님의 다른 주문을 맞히는 상황에 가깝다.

의료 데이터에서도 비슷하다. 한 환자에게서 여러 검사 기록이 나온 경우, 같은 환자의 기록이 학습과 테스트에 동시에 들어가면 모델 성능이 실제보다 좋게 보일 수 있다. 병원 단위로 차이가 큰 데이터라면 같은 병원의 기록이 양쪽에 섞이는 것도 문제가 될 수 있다.

그룹 단위 분할은 이런 상황을 막기 위해 같은 그룹을 통째로 한쪽에 배치한다. 고객 단위로 나누거나, 환자 단위로 나누거나, 매장 단위로 나누는 식이다. 어떤 그룹을 기준으로 삼을지는 모델이 실제로 만날 "새로운 대상"이 무엇인지에 따라 결정한다.

테스트 데이터는 금고에 넣어 둔다

테스트 데이터는 한 번 떼어 놓았다고 끝이 아니다. 더 중요한 원칙이 있다. 테스트 데이터는 모델 선택 과정에 반복적으로 사용하지 않아야 한다.

처음에는 이 말이 이상하게 들릴 수 있다. 모델을 여러 개 만들어 보고 테스트 점수가 가장 높은 모델을 고르면 되는 것 아닌가? 실제로 그렇게 하고 싶은 유혹이 크다. 선형회귀도 해 보고, 결정트리도 해 보고, 랜덤포레스트도 해 보고, 그래디언트 부스팅도 해 본다. 그런 다음 테스트 성능이 가장 좋은 모델을 고르면 깔끔해 보인다.

하지만 이 과정을 반복하면 테스트 데이터는 더 이상 순수한 시험지가 아니다. 우리는 테스트 점수를 보며 모델을 계속 바꾸게 된다. 그러면 모델은 직접적으로 테스트 데이터로 학습하지 않았더라도, 간접적으로 테스트 데이터에 맞춰진다. 이것을 흔히 테스트 데이터에 과적합된다고 표현한다.

학생이 모의고사 문제를 여러 번 풀고, 틀린 문제를 보고, 그 문제에 맞춰 공부법을 바꾸고, 다시 같은 모의고사를 푼다고 해 보자. 점수는 오를 수 있다. 하지만 그 점수가 새로운 시험에서도 유지된다고 장담하기는 어렵다. 머신러닝의 테스트 데이터도 마찬가지다.

그래서 테스트 데이터는 마지막 확인용으로 아껴 두어야 한다. 모델을 고르고, 설정을 조정하고, 특징을 바꾸는 과정에는 다른 데이터가 필요하다. 바로 다음 장에서 배울 검증 데이터가 그 역할을 한다.

지금 단계에서는 이렇게 구분하면 충분하다.

데이터 종류역할
학습 데이터모델이 규칙을 배우는 데 사용한다
검증 데이터여러 모델과 설정을 비교하고 고르는 데 사용한다
테스트 데이터최종 모델의 실전 성능을 마지막으로 추정하는 데 사용한다

이번 장의 주인공은 학습 데이터와 테스트 데이터지만, 실전 프로젝트에서는 검증 데이터까지 함께 생각해야 한다. 테스트 데이터를 자꾸 들여다보는 순간, 우리는 마지막 시험지를 연습장처럼 써 버리는 셈이다.

테스트 성능은 미래의 보증서가 아니라 추정치다

테스트 데이터에서 높은 성능이 나왔다고 해서 모델이 앞으로도 반드시 똑같이 잘한다는 뜻은 아니다. 테스트 성능은 어디까지나 현재 가지고 있는 데이터로 추정한 실전 성능이다.

왜 그럴까? 테스트 데이터도 결국 과거에 수집된 데이터의 일부이기 때문이다. 실제 서비스에서는 데이터의 성격이 바뀔 수 있다. 사용자의 취향이 달라지고, 시장 상황이 바뀌고, 스팸 메일 작성자들이 새로운 문구를 쓰기 시작한다. 집값 예측 모델이라면 금리, 정책, 지역 개발 계획 같은 외부 요인이 달라질 수 있다.

그래서 테스트 성능은 "이 모델은 영원히 이 정도로 잘할 것이다"라는 보증서가 아니다. 더 정확히 말하면 "우리가 숨겨 둔 데이터에서는 이 정도로 작동했다"라는 신호다. 좋은 신호일수록 안심할 수 있지만, 실전에 배포한 뒤에도 계속 관찰해야 한다.

이 관점은 머신러닝을 더 현실적으로 바라보게 해 준다. 모델 평가는 한 번의 의식이 아니라, 모델을 믿을 수 있는지 계속 확인하는 과정이다.

작은 프로젝트에서 적용해 보기

집값 예측 모델을 만든다고 해 보자. 전체 데이터에는 집의 면적, 방 개수, 위치, 건축 연도, 실제 거래 가격이 들어 있다. 우리는 데이터를 학습용과 테스트용으로 나눈다. 모델은 학습 데이터에서 "면적이 넓을수록 가격이 높아지는 경향이 있다", "특정 지역은 평균 가격이 높다", "건축 연도가 오래될수록 가격이 낮아질 수 있다" 같은 관계를 배운다.

그다음 테스트 데이터의 집 정보를 넣고 가격을 예측한다. 모델은 이 집들의 실제 거래 가격을 학습 중에는 본 적이 없다. 평가 단계에서만 예측 가격과 실제 가격을 비교한다. 이 차이가 작다면 모델은 처음 보는 집에도 어느 정도 적용될 가능성이 있다. 차이가 크다면 학습 데이터에서 배운 규칙이 충분하지 않거나, 과적합되었거나, 중요한 특징이 빠졌을 수 있다.

스팸 메일 분류기도 마찬가지다. 학습 데이터로는 메일 내용과 스팸 여부를 학습한다. 테스트 단계에서는 새 메일처럼 숨겨 둔 데이터를 넣어 스팸인지 정상인지 예측하게 한다. 학습 데이터에서는 거의 완벽했는데 테스트 데이터에서 많이 틀린다면, 모델이 특정 단어를 외웠거나 학습 데이터의 편향에 갇혔을 가능성이 있다.

이처럼 학습 데이터와 테스트 데이터의 분리는 모든 실습 프로젝트의 출발점이다. 집값 예측, 타이타닉 생존 예측, 고객 이탈 예측, 추천 시스템까지 문제는 달라도 원칙은 같다. 모델이 배운 데이터와 모델을 평가하는 데이터는 분리되어야 한다.

좋은 평가의 출발점

머신러닝에서 모델을 만드는 일은 단순히 높은 점수를 얻는 게임이 아니다. 더 중요한 목표는 처음 보는 데이터에서도 잘 작동하는 모델을 만드는 것이다. 그러려면 평가가 정직해야 한다.

정직한 평가는 모델을 불편하게 만든다. 이미 본 문제만 풀게 하지 않고, 처음 보는 문제 앞에 세운다. 이때 모델이 흔들리면 우리는 약점을 발견한다. 성능이 생각보다 낮게 나올 수도 있다. 하지만 그것이 더 좋은 출발점이다. 실전에 나가서 실패하는 것보다, 테스트 데이터 앞에서 미리 실패하는 편이 훨씬 낫다.

학습 데이터와 테스트 데이터를 나누는 일은 머신러닝 프로젝트에서 가장 기본적인 절차다. 동시에 가장 중요한 태도이기도 하다. 모델을 믿고 싶을수록, 모델에게 공정한 시험을 치르게 해야 한다.

다음 장에서는 여기서 한 걸음 더 나아간다. 테스트 데이터를 마지막까지 아껴 두려면, 모델을 고르고 조정하는 과정에는 무엇을 써야 할까? 그 답이 바로 검증 데이터와 교차검증이다.

핵심 정리

  • 학습 데이터는 모델이 규칙을 배우는 데 사용하고, 테스트 데이터는 최종 성능을 추정하는 데 사용한다.

  • 같은 데이터로 학습과 평가를 모두 하면 모델 성능을 실제보다 좋게 착각할 수 있다.

  • 데이터의 성격에 따라 무작위 분할, 시간 순서 분할, 그룹 단위 분할을 선택해야 한다.

  • 테스트 데이터는 모델 선택과 설정 조정에 반복적으로 사용하지 않아야 한다.

  • 테스트 성능은 실전 성능의 보증서가 아니라, 숨겨 둔 데이터로 확인한 추정치다.

책 목록

처음 만나는 머신러닝: 데이터가 스스로 규칙을 배우는 법

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기