테스트 데이터만으로는 부족하다
앞 장에서 우리는 데이터를 학습 데이터와 테스트 데이터로 나누는 이유를 배웠다. 학습 데이터는 모델이 규칙을 배우는 재료이고, 테스트 데이터는 모델이 처음 보는 문제를 얼마나 잘 푸는지 확인하는 마지막 시험지다. 여기까지는 꽤 단순해 보인다.
그런데 실제로 모델을 만들기 시작하면 곧 이런 질문이 생긴다.
어떤 모델을 써야 할까? 선형회귀가 좋을까, 랜덤포레스트가 좋을까? 결정트리의 깊이는 어느 정도가 적당할까? 그래디언트 부스팅은 얼마나 많은 트리를 쌓아야 할까? 로지스틱 회귀의 규제 강도는 어떻게 정해야 할까?
이 질문에 답하려면 여러 후보 모델을 만들어 비교해야 한다. 문제는 여기서 테스트 데이터를 자꾸 사용하면 안 된다는 점이다. 테스트 데이터는 마지막까지 아껴 두어야 하는 최종 확인용 데이터다. 그런데 모델을 고를 때마다 테스트 성능을 들여다보면, 우리는 무의식적으로 테스트 데이터에 잘 맞는 모델을 고르게 된다. 겉으로는 공정한 시험처럼 보이지만, 실제로는 시험지를 여러 번 훔쳐보며 답안을 고치는 것과 비슷해진다.
그래서 필요한 것이 검증 데이터다. 검증 데이터는 학습 중간에 모델 후보를 비교하기 위해 따로 떼어 둔 데이터다. 모델이 직접 학습하지는 않지만, 어떤 모델 설정이 더 나은지 판단할 때 사용한다. 다시 말해 검증 데이터는 연습 시험이고, 테스트 데이터는 최종 시험이다.

세 종류의 데이터가 맡는 역할
머신러닝 실험에서 데이터를 세 부분으로 나누면 각자의 역할이 선명해진다.
학습 데이터는 모델이 패턴을 배우는 데 사용한다.
검증 데이터는 모델 종류와 설정을 비교하는 데 사용한다.
테스트 데이터는 모든 선택이 끝난 뒤 최종 성능을 확인하는 데 사용한다.
예를 들어 스팸 메일 분류기를 만든다고 해 보자. 우리는 먼저 전체 메일 데이터를 학습, 검증, 테스트 데이터로 나눈다. 모델은 학습 데이터에 있는 단어 패턴을 보며 '무료', '당첨', '지금 클릭' 같은 표현이 스팸과 얼마나 관련 있는지 배운다. 그다음 검증 데이터로 여러 후보를 비교한다. 로지스틱 회귀가 나은지, 결정트리가 나은지, 랜덤포레스트가 나은지 살펴본다. 또한 결정트리를 쓴다면 트리의 깊이를 3으로 할지, 5로 할지, 10으로 할지도 비교한다.
이 과정이 끝나면 가장 좋아 보이는 모델 하나를 고른다. 그리고 마지막으로 테스트 데이터에 딱 한 번 가까운 마음으로 적용해 본다. 이때 나온 성능이 우리가 기대할 수 있는 실제 성능에 더 가깝다.
물론 현실에서는 테스트 데이터를 정말 딱 한 번만 보는 것이 늘 가능한 것은 아니다. 프로젝트가 길어지면 여러 실험을 하며 다시 확인하고 싶은 유혹이 생긴다. 하지만 원칙은 분명하다. 테스트 데이터는 모델 선택의 도구가 아니라 최종 확인의 도구다.
검증 데이터는 모델의 면접장이다
검증 데이터를 이해하는 가장 쉬운 방법은 면접장에 비유하는 것이다. 학습 데이터가 학교라면, 검증 데이터는 면접장이다. 모델은 학교에서 공부하고, 면접장에서 실력을 보여 준다. 우리는 면접 결과를 보고 어떤 후보를 뽑을지 결정한다. 그러나 최종 업무 능력은 실제 현장, 즉 테스트 데이터에서 확인한다.
모델을 선택할 때 비교하는 것은 크게 두 가지다. 하나는 모델의 종류이고, 다른 하나는 하이퍼파라미터다. 하이퍼파라미터는 모델이 학습으로 스스로 알아내는 값이 아니라, 사람이 미리 정해 주는 설정값이다. 이름은 조금 어렵지만 뜻은 단순하다. 모델의 성격을 정하는 손잡이라고 생각하면 된다.
결정트리에서 최대 깊이는 대표적인 하이퍼파라미터다. 트리가 너무 얕으면 데이터의 중요한 차이를 충분히 나누지 못한다. 반대로 너무 깊으면 학습 데이터의 사소한 우연까지 외워 버릴 수 있다. 랜덤포레스트에서는 트리의 개수, 각 트리가 볼 특징의 수 등이 하이퍼파라미터가 된다. K-means에서는 군집 수 K가 하이퍼파라미터다.
검증 데이터는 이 손잡이를 어디에 맞출지 판단하게 해 준다. 학습 데이터에서 성능이 가장 좋은 설정이 항상 좋은 것은 아니다. 학습 데이터만 보면 깊은 결정트리가 거의 언제나 유리하다. 깊게 파고들수록 학습 데이터를 더 세세하게 외울 수 있기 때문이다. 하지만 검증 데이터에서 성능이 떨어진다면, 그 모델은 규칙을 배운 것이 아니라 문제지를 외운 것일 가능성이 크다.
모델 선택의 기본 절차
실전에서는 보통 다음과 같은 흐름으로 실험한다.
전체 데이터를 학습, 검증, 테스트 데이터로 나눈다.
학습 데이터로 여러 후보 모델을 훈련한다.
검증 데이터 성능을 보고 모델 종류와 하이퍼파라미터를 고른다.
선택이 끝나면 학습 데이터와 검증 데이터를 합쳐 최종 모델을 다시 훈련할 수 있다.
마지막으로 테스트 데이터에서 최종 성능을 확인한다.
마지막에서 학습 데이터와 검증 데이터를 합쳐 다시 훈련할 수 있다는 말이 조금 낯설 수 있다. 왜냐하면 모델 선택이 끝난 뒤에는 검증 데이터의 역할이 일단 끝났기 때문이다. 이미 어떤 모델을 쓸지, 어떤 설정을 쓸지 결정했다면, 이제는 사용 가능한 데이터를 최대한 활용해 최종 모델을 만들 수 있다. 단, 이때도 테스트 데이터는 여전히 학습에 넣으면 안 된다.
예를 들어 집값 예측 모델을 만든다고 하자. 전체 데이터가 10,000건 있다면 7,000건은 학습 데이터, 1,500건은 검증 데이터, 1,500건은 테스트 데이터로 나눌 수 있다. 우리는 7,000건으로 여러 모델을 학습시키고, 1,500건의 검증 데이터로 성능을 비교한다. 최종적으로 랜덤포레스트가 가장 안정적이라고 판단했다면, 학습 데이터와 검증 데이터를 합친 8,500건으로 랜덤포레스트를 다시 학습시킨다. 그리고 마지막 1,500건의 테스트 데이터로 최종 점수를 확인한다.
이 절차의 핵심은 단순하다. 모델을 고르는 데 사용한 데이터와 최종 성능을 확인하는 데이터를 분리해야 한다.
데이터가 적을 때 생기는 고민
검증 데이터는 유용하지만, 한 가지 고민이 있다. 데이터가 충분히 많지 않으면 검증 데이터를 따로 떼어 두는 것 자체가 부담이 된다. 전체 데이터가 1,000건밖에 없는데 그중 200건을 검증용, 200건을 테스트용으로 빼면 학습에는 600건만 남는다. 모델이 배울 재료가 줄어드는 셈이다.
또 다른 문제도 있다. 검증 데이터를 어떻게 나누었느냐에 따라 성능이 흔들릴 수 있다. 우연히 쉬운 데이터가 검증 세트에 많이 들어가면 성능이 높게 나온다. 반대로 어려운 데이터가 검증 세트에 몰리면 성능이 낮게 나온다. 그러면 우리는 모델이 정말 좋은지, 아니면 운 좋게 검증 데이터와 잘 맞았는지 판단하기 어려워진다.
이때 사용하는 방법이 교차검증이다. 교차검증은 데이터를 여러 조각으로 나눈 뒤, 각 조각을 돌아가며 검증 데이터로 사용하는 방법이다. 하나의 검증 세트에 모든 판단을 맡기지 않고, 여러 번의 검증 결과를 모아 더 안정적인 성능을 추정한다.
K-겹 교차검증의 직관
가장 많이 쓰이는 방식은 K-겹 교차검증이다. 영어로는 K-fold cross-validation이라고 부른다. 여기서 K는 데이터를 몇 조각으로 나눌지를 뜻한다. 예를 들어 K가 5라면 전체 학습용 데이터를 5개의 비슷한 크기 조각으로 나눈다.
그다음 첫 번째 조각을 검증 데이터로 쓰고, 나머지 네 조각으로 모델을 학습한다. 성능을 기록한다. 이번에는 두 번째 조각을 검증 데이터로 쓰고, 나머지 네 조각으로 다시 학습한다. 이런 식으로 다섯 번째 조각까지 한 번씩 검증 역할을 맡긴다. 그러면 총 5개의 성능 점수가 나온다.
이 과정을 그림 대신 말로 펼쳐 보면 다음과 같다.
1회차: 1번 조각을 검증에 쓰고, 2·3·4·5번 조각으로 학습한다.
2회차: 2번 조각을 검증에 쓰고, 1·3·4·5번 조각으로 학습한다.
3회차: 3번 조각을 검증에 쓰고, 1·2·4·5번 조각으로 학습한다.
4회차: 4번 조각을 검증에 쓰고, 1·2·3·5번 조각으로 학습한다.
5회차: 5번 조각을 검증에 쓰고, 1·2·3·4번 조각으로 학습한다.
이렇게 하면 모든 데이터가 한 번씩 검증 데이터가 되고, 동시에 대부분의 회차에서는 학습 데이터로도 사용된다. 데이터가 적을 때 특히 유리한 이유가 여기에 있다. 한 번의 분할에 모든 판단을 걸지 않고, 여러 방향에서 모델을 시험해 보는 것이다.
평균만 보면 놓치는 것
교차검증을 하면 여러 개의 성능 점수가 나온다. 예를 들어 어떤 모델의 5-겹 교차검증 정확도가 다음과 같다고 해 보자.
0.82, 0.81, 0.83, 0.80, 0.82
평균은 약 0.816이다. 꽤 안정적으로 보인다. 이번에는 다른 모델이 다음과 같은 점수를 얻었다고 해 보자.
0.91, 0.74, 0.88, 0.69, 0.86
이 모델의 평균은 약 0.816으로 비슷하다. 하지만 느낌은 전혀 다르다. 어떤 조각에서는 매우 잘 맞고, 어떤 조각에서는 크게 흔들린다. 평균만 보면 두 모델이 비슷해 보이지만, 두 번째 모델은 데이터가 조금만 바뀌어도 성능이 출렁일 가능성이 크다.
그래서 교차검증 결과를 볼 때는 평균과 변동성을 함께 봐야 한다. 평균은 모델이 대체로 얼마나 잘하는지 알려 준다. 변동성은 성능이 얼마나 안정적인지 알려 준다. 좋은 모델은 평균 성능이 높을 뿐 아니라, 여러 검증 조각에서 지나치게 흔들리지 않는다.
실무에서는 성능이 아주 조금 높은 모델보다, 약간 낮더라도 안정적인 모델을 선택하는 경우가 많다. 특히 의료, 금융, 안전 관련 예측처럼 실수가 큰 비용으로 이어지는 문제에서는 더 그렇다. 한 번은 천재처럼 보이고 한 번은 완전히 무너지는 모델보다, 매번 꾸준히 괜찮은 모델이 더 믿음직할 수 있다.
검증은 모델을 믿기 위한 과정이다
머신러닝을 처음 배울 때는 성능 숫자 하나에 마음이 쉽게 흔들린다. 정확도 91%라는 숫자를 보면 모델이 훌륭해 보인다. 평균제곱오차가 줄어들면 뭔가 제대로 되고 있는 것 같다. 하지만 숫자는 항상 맥락과 함께 봐야 한다. 그 숫자가 학습 데이터에서 나온 것인지, 검증 데이터에서 나온 것인지, 테스트 데이터에서 나온 것인지에 따라 의미가 완전히 달라진다.
학습 데이터 성능은 모델이 배운 내용을 얼마나 잘 따라 하는지 보여 준다. 검증 데이터 성능은 여러 후보 중 어떤 선택이 더 나은지 판단하게 해 준다. 테스트 데이터 성능은 마지막까지 숨겨 둔 데이터에서 모델이 얼마나 버티는지 보여 준다.
여기서 중요한 태도는 '좋은 점수를 만들기'가 아니라 '믿을 만한 점수를 얻기'다. 모델 개발은 시험 점수를 억지로 끌어올리는 게임이 아니다. 앞으로 들어올 새로운 데이터에서 어느 정도의 성능을 기대할 수 있는지 정직하게 추정하는 과정이다.
검증 데이터도 남용하면 오염된다
테스트 데이터를 자주 보면 안 된다고 했지만, 사실 검증 데이터도 무한히 믿을 수 있는 것은 아니다. 같은 검증 데이터로 수십, 수백 번 실험을 반복하면 결국 그 검증 데이터에 잘 맞는 설정을 찾게 된다. 이것은 테스트 데이터를 반복해서 보는 문제와 닮아 있다. 다만 검증 데이터는 애초에 모델 선택을 위해 마련한 데이터이므로 어느 정도의 반복 사용은 자연스럽다.
문제는 검증 성능에 지나치게 맞추기 시작할 때 생긴다. 검증 점수가 0.1%라도 오르는 설정을 계속 찾아다니다 보면, 실제로 일반화 성능이 좋아진 것이 아니라 검증 세트의 우연한 성질에 맞춰진 것일 수 있다. 이런 상황을 피하려면 실험 목적을 분명히 하고, 너무 작은 차이에 과도한 의미를 부여하지 않아야 한다.
예를 들어 두 모델의 검증 정확도가 0.842와 0.845라면, 후자가 반드시 더 좋은 모델이라고 단정하기 어렵다. 데이터 분할이 조금만 달라져도 순위가 바뀔 수 있다. 반면 한 모델은 평균 0.84에 매우 안정적이고, 다른 모델은 평균 0.85지만 성능 변동이 크다면 어느 쪽을 선택할지는 문제의 성격에 따라 달라진다.
분류 문제에서는 비율도 신경 써야 한다
교차검증을 할 때 한 가지 더 조심할 점이 있다. 분류 문제에서는 각 조각에 정답 클래스의 비율이 비슷하게 들어가야 한다. 예를 들어 스팸 메일이 전체의 10%인 데이터가 있다고 하자. 그런데 어떤 검증 조각에는 스팸이 거의 없고, 다른 조각에는 스팸이 몰려 있다면 성능 비교가 불안정해진다.
이럴 때는 계층적 K-겹 교차검증을 사용한다. 이름은 어렵지만 원리는 간단하다. 전체 데이터의 클래스 비율을 각 조각에서도 최대한 비슷하게 유지하는 방법이다. 스팸이 전체의 10%라면 각 fold에도 스팸이 대략 10%씩 들어가도록 나눈다. 특히 데이터가 불균형할수록 이런 방식이 도움이 된다.
다만 모든 데이터에 무작위 분할을 적용할 수 있는 것은 아니다. 시간 순서가 중요한 데이터에서는 미래 데이터가 과거 데이터의 학습에 섞이면 안 된다. 예를 들어 다음 달 매출을 예측하는 모델을 만들면서 미래의 정보를 학습 과정에 넣으면 현실에서는 불가능한 예측이 된다. 이런 경우에는 시간 순서를 지키며 검증하는 별도의 방식이 필요하다. 자세한 데이터 누수 문제는 뒤에서 다시 다룰 것이다.
검증 절차가 만들어 주는 안정감
검증 데이터와 교차검증은 머신러닝에서 약간 귀찮아 보이는 절차다. 데이터를 나누고, 여러 번 학습하고, 평균을 내고, 변동성을 확인해야 한다. 하지만 이 과정은 모델에 대한 신뢰를 만들어 준다.
한 번 운 좋게 잘 나온 모델과 여러 조건에서 꾸준히 좋은 모델은 다르다. 검증 절차를 제대로 거치면 우리는 단순히 '이번에는 잘 맞았다'가 아니라 '여러 번 확인해도 꽤 잘 맞는다'고 말할 수 있다. 이 차이가 실험 장난감과 실제 서비스 모델을 가른다.
머신러닝의 기본 흐름을 다시 떠올려 보자. 데이터를 준비하고, 특징을 만들고, 모델이 예측하고, 실제값과 비교하고, 오차를 줄이고, 새로운 데이터에 적용한다. 검증 데이터와 교차검증은 이 마지막 부분, 즉 새로운 데이터에 적용했을 때도 잘 작동할지를 미리 점검하는 장치다.
모델은 학습 데이터 안에서만 살아서는 안 된다. 우리가 원하는 모델은 아직 만나지 못한 데이터 앞에서도 침착하게 답을 내놓는 모델이다. 검증은 그 침착함을 확인하는 연습 무대이고, 테스트는 그 무대가 끝난 뒤의 최종 리허설이다.
다음 장에서는 모델이 사용할 재료 자체를 더 좋게 만드는 방법을 살펴본다. 같은 모델이라도 어떤 특징을 넣느냐에 따라 성능은 크게 달라진다. 이제 우리는 좋은 평가 절차를 갖추었으니, 다음에는 모델이 더 잘 배울 수 있도록 데이터를 다듬는 법, 즉 특성 엔지니어링으로 넘어갈 차례다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.