단원 홈
2단원 · 11차시

외운 것인가 이해한 것인가
훈련 정확도 100%라는 나쁜 소식

8차시에서 여러분은 k-NN을 직접 짰습니다. 오늘은 그 코드를 한 글자도 고치지 않은 채, 채점하는 법만 바꿉니다. 같은 모델이 100%도 되고 60%도 되는 것을 직접 보게 될 거예요.

성취기준 12인기02-04
훈련 데이터테스트 데이터과적합 과소적합검증 데이터교차 검증
🎯 학습 목표
  • 훈련 데이터 · 검증 데이터 · 테스트 데이터의 역할을 구분하고, 학습에 쓴 데이터로 채점하면 왜 점수가 부풀려지는지 설명할 수 있다.
  • 8차시의 k-NN을 그대로 써서 k를 1부터 45까지 바꿔 가며 두 정확도를 재고, k=1의 훈련 정확도가 100.0%인 까닭을 코드 한 줄로 무너뜨려 보일 수 있다.
  • 한 번의 분할이 20.0%p나 흔들린다는 것을 수치로 확인하고, 검증 데이터와 교차 검증이 왜 따로 필요한지 근거를 들어 말할 수 있다.
🤔

여는 장면 — "내 모델, 정확도 100%야"

옆자리 친구가 노트북을 돌려 보이며 말합니다. "합격자 예측 모델 만들었는데, 정확도가 100%야."

축하할 일일까요? 잠깐 멈춰 봅시다. 그 100%는 무엇을 맞힌 점수일까요? 친구가 모델에게 보여 준 것은 학습할 때 이미 본 60명이었습니다. 기출문제를 외운 사람에게 그 기출문제를 그대로 내 준 셈이지요. 100점이 나온 것은 당연하고, 그 100점은 내일 시험을 잘 볼 것이라는 근거가 되지 못합니다.

이것이 오늘 다룰 이야기입니다. 기계학습에서 모델을 만드는 일과 모델을 채점하는 일은 별개이고, 채점을 잘못하면 쓸모없는 모델을 훌륭하다고 착각하게 됩니다. 더 나쁜 것은, 잘못된 채점이 늘 기분 좋은 숫자를 준다는 점이에요. 틀렸다는 신호조차 오지 않습니다.

오늘 쓸 데이터는 60명의 두 점수와 합격 여부입니다. 규칙은 단순합니다 — 두 점수의 합이 10을 넘으면 합격. 다만 현실의 기록이 늘 그렇듯, 약 15%는 잘못 적혔습니다. 합격인데 불합격으로, 불합격인데 합격으로 뒤집혀 있어요. 실제로 세어 보면 60명 중 10명이 뒤집혀 있습니다(15%를 60번 굴린 결과라 딱 9명이 되지는 않습니다).

💭 오늘의 물음

훈련 정확도 100%는 좋은 소식인가 나쁜 소식인가? 그리고 모델의 진짜 실력은 무엇으로 재야 하는가?

미리 답을 하나 흘려 두겠습니다. 오늘 우리가 쓸 k-NN에서, k=1로 두면 훈련 정확도는 언제나 100.0%가 나옵니다. 데이터를 바꿔도, 씨앗을 바꿔도, 잡음을 절반까지 올려 배울 것이 하나도 없게 만들어도 그렇습니다. 구조가 정해 버리는 값이라 흔들리지 않아요. 그 이유를 스스로 알아내는 것이 오늘 수업의 절반입니다.

1

채점지를 미리 떼어 놓는다

해결책은 어이없을 만큼 간단합니다. 데이터를 미리 두 덩이로 가르는 것이에요.

  • 훈련 데이터 — 모델이 보고 배우는 몫. 마음껏 써도 됩니다.
  • 테스트 데이터 — 학습에 한 번도 쓰지 않고 금고에 넣어 두는 몫. 맨 마지막에 딱 한 번, 성적표를 적을 때만 꺼냅니다.

가르는 비율은 대개 훈련 60~80%입니다. 오늘 실습은 60명을 훈련 45명 · 테스트 15명(75% / 25%)으로 가릅니다. 가르기 전에 반드시 섞어야 해요. 기록이 합격자부터 차례로 적혀 있었다면, 앞에서 45명을 자르는 순간 훈련에는 합격만, 테스트에는 불합격만 들어갑니다.

모아 놓은 기록 60명 합격 37 · 불합격 23 섞고 훈련 데이터 45명 합격 30 · 불합격 15 · 잘못 적힌 기록 9 테스트 데이터 15명 🔒 합격 7 · 불합격 8 · 잘못 적힌 기록 1 모델이 배운다 아직 열지 않는다 맨 마지막에 한 번 ⚠️ 가장 흔한 실수 훈련 데이터로 배우고 훈련 데이터로 채점한다 — 기출문제를 외운 사람에게 그 기출문제를 낸다. 점수는 늘 높게 나오고, 틀렸다는 신호는 오지 않는다.
60명을 섞은 뒤 45명과 15명으로 가른다. 오른쪽 아래 상자는 학습이 끝날 때까지 잠겨 있다. 숫자는 오늘 실습 데이터의 실제 값이다.

가르고 나면 정확도를 두 개 잴 수 있게 됩니다. 8차시에서 만든 accuracy()는 한 글자도 고치지 않습니다. 무엇을 넣느냐만 달라져요.

📒 훈련 정확도

  • 배운 것과 채점받는 것이 같다
  • accuracy(훈련, 훈련, k)
  • 모델이 데이터를 얼마나 잘 담아 두었는지를 잰다
  • 높다고 좋은 것이 아니다
vs

🔒 테스트 정확도

  • 배운 것과 채점받는 것이 다르다
  • accuracy(훈련, 테스트, k)
  • 모델이 처음 보는 사람을 맞히는지를 잰다
  • 우리가 알고 싶은 값은 이쪽이다

두 함수의 몸통은 각각 한 줄입니다. 그 한 줄이 오늘 배우는 전부예요. 실습 코드에서 여러분이 채울 첫 두 빈칸이 바로 이 자리입니다.

⚠️ 조용히 틀리는 자리

테스트 정확도를 accuracy(테스트, 테스트, k)로 쓰면 어떻게 될까요? 테스트 데이터로 학습해 버립니다. 그러면 k=1에서 또 100%가 나오고, 그게 정상처럼 보입니다. 오류 메시지도 없어요.

기계학습의 실수는 대개 이런 모양입니다. 프로그램이 멈추지 않고, 숫자는 오히려 더 좋아집니다. 그래서 숫자가 좋아졌을 때 한 번 더 의심하는 습관이 필요합니다.

테스트가 15명뿐이라는 사실

여기서 미리 못 박아 둘 것이 하나 있습니다. 테스트가 15명이면 한 명이 6.7%p입니다. 뒤에 나올 표에서 "k=3이 k=5보다 6.7%p 좋다"는 말은 사실 한 명 차이예요. 점수 차이를 볼 때마다 그것이 몇 명인지를 되물어야 합니다.

그렇다고 테스트를 줄이면 될까요? 반대입니다. 비율을 바꿔 재 보면 이렇습니다.

훈련 비율훈련테스트k=1 테스트k=3 테스트한 명의 무게
50%30명30명73.3%80.0%3.3%p
60%36명24명66.7%70.8%4.2%p
75% (오늘)45명15명60.0%66.7%6.7%p
90%54명6명83.3%83.3%16.7%p
같은 60명, 같은 코드. 가르는 비율만 바꿨다. 90%로 하면 점수가 확 오르지만 그것은 실력이 아니라 눈금이 거칠어진 것이다 — 테스트가 6명뿐이라 한 명이 16.7%p다. 네 비율 모두 k=1의 훈련 정확도는 100.0%였다.

훈련을 늘리면 모델은 잘 배우지만 채점이 거칠어지고, 테스트를 늘리면 채점은 정밀해지지만 배울 것이 줄어듭니다. 60~80%라는 관례는 이 둘 사이의 타협이에요. 마법의 숫자가 아닙니다.

ℹ️ 오늘은 왜 정규화를 안 하나

5차시에서 걸음 수와 공부 시간처럼 단위가 다른 특성을 그냥 재면 큰 쪽이 거리를 독차지한다는 것을 보았지요(가람·나래·다온의 순위가 통째로 뒤집혔습니다). 오늘 두 특성은 둘 다 0~10점이라 자리가 같습니다. 그래서 정규화가 필요 없습니다. 정규화는 늘 해야 하는 의식이 아니라 단위가 어긋났을 때 쓰는 도구입니다.

2

k=1의 훈련 정확도가 100%인 진짜 이유

8차시에서 k-NN이 하는 일을 세 걸음으로 적었지요.

  1. 물어볼 점과 훈련 데이터 모든 점 사이의 거리를 잰다
  2. 가까운 순으로 줄을 세우고 앞에서 k개를 뽑는다
  3. 뽑힌 k개의 이름표를 세어 다수결로 답한다

이제 훈련 데이터에 있는 사람을 그대로 다시 물어본다고 해 봅시다. 1번 걸음에서 거리를 재는데, 줄에 자기 자신도 서 있습니다. 자기와 자기 사이의 거리는 0이에요. 이보다 가까운 사람은 있을 수 없습니다.

그러니 1등 이웃은 언제나 자기 자신입니다. k=1이면 뽑히는 이웃이 그 하나뿐이고, 그 이웃의 이름표는 곧 자기 이름표입니다. k=1은 "너는 누구냐"는 물음에 "너다"라고 답하는 셈이에요. 100%가 나올 수밖에 없습니다.

실습 코드가 훈련 데이터 세 사람에게 실제로 물어본 결과입니다.

훈련 데이터이름표1등 이웃까지의 거리그 이웃의 이름표2등까지의 거리
1번째 사람10.00010.633
2번째 사람00.00001.732
3번째 사람10.00011.386
1등 거리가 셋 다 정확히 0.000이다. 자기 자신이다. 2등부터는 0.633 · 1.732 · 1.386으로 벌어진다 — 진짜 이웃은 그 사람들이다.

말로 설명하지 말고 무너뜨려 보자

"자기 자신 때문이다"라는 설명은 들으면 그럴듯합니다. 하지만 그럴듯한 설명은 다음 주면 잊힙니다. 대신 코드 한 줄을 고쳐서 그 100%가 실제로 무너지는지 봅시다.

줄을 세우기 전에 자기 자신만 빼는 것입니다. 나머지 44명에게만 물어보게 하는 거예요. 이 방식에는 하나 빼고 재기(leave-one-out)라는 이름이 붙어 있습니다.

k그냥 재면 (훈련 정확도)자기 자신을 빼면떨어진 폭
1100.0%64.4%-35.6%p
384.4%62.2%-22.2%p
575.6%66.7%-8.9%p
975.6%73.3%-2.3%p
훈련 45명, 본문과 같은 기본 분할. 자기 자신을 빼자마자 k=1의 100.0%가 64.4%로 내려앉는다. k가 커질수록 두 값이 붙는다 — 자기 표 한 장의 무게가 1/k로 줄기 때문이다.

이 네 줄이 오늘의 핵심 그림입니다. 읽는 법은 이렇습니다.

  • k=1의 100.0%는 실력이 아니라 반칙이었다. 자기 표를 빼자 64.4%로 무너집니다.
  • k=9에서는 두 값이 2.3%p밖에 차이 나지 않는다. 이웃이 아홉이면 자기 표 한 장은 1/9이라 다수결을 뒤집기 어렵습니다. 그래서 k가 클 때의 훈련 정확도는 어느 정도 믿을 만한 숫자가 됩니다.
  • 그래도 훈련 정확도는 여전히 실제보다 후하다. 네 줄 모두 오른쪽이 왼쪽보다 낮습니다.
💡 여기가 이 차시의 매듭

k=1의 훈련 정확도 100.0%는 데이터에 대해 아무것도 말해 주지 않습니다. 씨앗을 바꿔도, 데이터를 20명에서 160명으로 늘려도, 잡음을 절반까지 올려 이름표를 거의 동전 던지기로 만들어도 언제나 100.0%가 나옵니다. 흔들리지 않는 숫자는 정보를 담고 있지 않습니다. 구조가 정해 버린 값이기 때문이에요.

3

과적합과 과소적합 — 두 곡선이 갈라지는 자리

이제 k를 1부터 45까지 바꿔 가며 두 정확도를 나란히 재 봅시다. k가 무엇을 조절하는지 먼저 짚고 가야 표가 읽힙니다.

🔍 k가 작다 = 복잡한 모델

  • 이웃 하나·셋의 말만 듣는다
  • 점 하나하나에 반응한다
  • 잘못 적힌 기록 한 명이 그 주변을 통째로 바꾼다
  • 경계선이 들쭉날쭉해진다
1/k

🌫️ k가 크다 = 단순한 모델

  • 이웃 스물다섯·마흔다섯의 평균을 본다
  • 잘못 적힌 기록 한 명은 묻힌다
  • 대신 진짜 무늬도 같이 뭉개진다
  • 경계선이 밋밋해진다

그래서 k-NN에서 모델의 복잡도는 1/k입니다. k를 줄이는 것이 복잡도를 올리는 것이고, k를 키우는 것이 모델을 단순하게 만드는 것이에요. 이 한 문장을 손에 쥐고 아래 표를 보세요.

k훈련 정확도테스트 정확도격차읽는 법
1100.0%60.0%40.0%p훈련만 완벽 — 외웠다
384.4%66.7%17.8%p이 분할에서는 여기가 최고
575.6%60.0%15.6%p
775.6%60.0%15.6%p
975.6%60.0%15.6%p완만한 구간
1177.8%60.0%17.8%p
1577.8%60.0%17.8%p
2175.6%46.7%28.9%p여기서 테스트가 무너진다
2568.9%46.7%22.2%p둘 다 같이 내려간다
3166.7%46.7%20.0%p
4566.7%46.7%20.0%p기준선 — 아무것도 안 배운 값
훈련 45명 · 테스트 15명, 한 번의 분할. 테스트 정확도 60.0% = 9명, 66.7% = 10명, 46.7% = 7명이다. 표의 모든 차이는 한두 명짜리임을 잊지 말 것.

표에서 읽어야 할 것 넷

① 훈련 정확도는 k=1에서 최고, 그리고 k가 커질수록 내려간다. 즉 훈련 정확도로 k를 고르면 언제나 k=1을 고르게 됩니다. 훈련 정확도는 k를 고르는 데 쓸 수 없는 숫자예요.

② 테스트 정확도는 올라갔다가 내려온다. k=3에서 66.7%로 정점을 찍고, k=21부터 46.7%로 주저앉습니다. 두 곡선이 이렇게 다르게 움직인다는 것이 오늘 그림의 전부입니다.

③ 왼쪽 끝이 과적합(overfitting)이다. k가 3에서 1로 내려가는 순간 격차가 17.8%p에서 40.0%p로 두 배 넘게 벌어집니다. 훈련 정확도만 오르고 테스트 정확도는 오히려 떨어지는 구간 — 외운 것입니다.

④ 오른쪽 끝이 과소적합(underfitting)이다. k=21부터는 훈련도 테스트도 같이 내려갑니다. k=45는 훈련 45명 전부가 투표하니 누구를 물어도 다수결(합격)만 답하는 모델이에요. 그 점수 훈련 66.7% · 테스트 46.7%가 곧 2차시에서 배운 기준선입니다 — 아무것도 배우지 않은 모델의 점수. k=25(68.9% / 46.7%)가 사실상 여기와 다를 게 없다는 것도 한눈에 보이지요.

빨간 점과 파란 점이 섞인 평면에 두 경계선이 그어져 있다. 초록 선은 점 사이를 이리저리 휘감으며 구불거리고, 검은 선은 완만한 곡선 하나로 두 무리를 가른다. 검은 테두리를 두른 큰 점 몇 개가 흩어져 있다
같은 점들, 다른 경계선. 초록 선은 빨간 점과 파란 점 사이를 하나하나 휘감으며 구불거리고, 검은 선은 완만하게 두 무리를 가른다. 검은 테두리를 두른 큰 점은 나중에 새로 들어온 점인데, 초록 선은 이들 상당수를 엉뚱한 쪽에 두고 검은 선은 제자리에 둔다. 우리 실습의 k=1이 그리는 것이 바로 초록 선이다. 출처: Chabacano, Wikimedia Commons (CC BY-SA 4.0)
나란히 놓인 두 그래프. 왼쪽 train set, 오른쪽 validation set에 파란 점들이 흩어져 있고, 주황 직선(Linear Model)과 초록 점선 곡선(Polynomial Model)이 겹쳐 그려져 있다. 범례의 오차는 왼쪽이 직선 3.222 · 다항식 2.813, 오른쪽이 직선 4.227 · 다항식 4.944
회귀에서도 같은 일이 일어난다. 초록 점선(다항식)은 구불거리며 점을 쫓아가고, 양 끝에서는 그래프 밖으로 치솟는다. 왼쪽 훈련 세트에서는 다항식의 오차(2.813)가 직선(3.222)보다 작지만, 오른쪽 검증 세트에서는 거꾸로 다항식(4.944)이 직선(4.227)보다 크다. 훈련 점수가 좋은 쪽이 새 데이터에서는 진다 — 오늘 k=1에서 본 그림 그대로다. 출처: LiamHuang, Wikimedia Commons (CC BY-SA 4.0)
⚠️ "작은 k는 나쁘다"로 외우지 말 것

이 표만 보면 "k=1은 나쁘다"가 결론 같습니다. 그런데 데이터를 만드는 씨앗을 3으로 바꾸면 k=1의 테스트 정확도가 93.3%로, k=3(80.0%)보다 오히려 높습니다. 잡음을 아예 0으로 두면 k=1이 모든 k 가운데 가장 좋습니다(뒤 5절에서 재 봅니다).

정확한 문장은 이것입니다 — "잡음이 있는 데이터에서 k가 너무 작으면 잡음을 외운다." 조건을 떼어 내고 결론만 외우면 다음 데이터에서 틀립니다.

4

한 번의 분할을 믿지 마라

3절의 표를 보고 "k=3을 쓰자"고 결론 내리고 싶어집니다. 그런데 그 표는 60명을 한 번 섞어 가른 결과 하나뿐이에요. 섞는 방법을 바꾸면 어떻게 될까요? 데이터도 코드도 그대로 두고, 가르는 방법만 12번 바꿔 봤습니다.

분할k=3의 테스트 정확도이번 분할에서 가장 좋았던 k그 점수
씨앗 173.3%986.7%
씨앗 280.0%180.0%
씨앗 380.0%180.0%
씨앗 460.0%173.3%
씨앗 580.0%380.0%
씨앗 660.0%1586.7%
씨앗 766.7%993.3%
씨앗 880.0%793.3%
씨앗 980.0%1593.3%
씨앗 1073.3%586.7%
씨앗 1180.0%380.0%
씨앗 1273.3%993.3%
같은 60명 · 같은 코드. 바꾼 것은 어떻게 섞어 가르느냐뿐이다. k=3의 테스트 정확도는 최저 60.0% · 최고 80.0% · 평균 73.9%, 흔들림 폭 20.0%p. '가장 좋았던 k'도 12번 중 여섯 가지가 나왔다 (1이 세 번, 9가 세 번, 3과 15가 두 번, 5와 7이 한 번씩).

정리하면 이렇습니다. 한 번 갈라 보고 k를 고르는 것은, 동전을 한 번 던져 보고 "이 동전은 앞면이 잘 나온다"고 말하는 것과 같습니다. 다음 분할에서는 절반 넘는 확률로 다른 k가 최선이 됩니다.

테스트 점수를 보고 k를 고르면 안 되는 이유

더 무서운 일이 있습니다. 위 표의 세 번째 칸 — '이번 분할에서 가장 좋았던 k' — 을 보고 k를 고른 뒤, 그 점수를 그대로 성적표에 적으면 어떻게 될까요?

k를 고르는 방식보고되는 테스트 정확도
미리 정한 k=3을 그대로 쓴다73.9%
테스트 점수를 보고 그 분할의 최고 k를 고른다85.6%
차이 (부풀려진 양)11.7%p
분할 12개의 평균. 모델을 더 좋게 만들지 않았는데 성적이 11.7%p 올랐다. 테스트 데이터를 보면서 고른 순간, 테스트는 이미 '처음 보는 데이터'가 아니다.

여기서 세 번째 데이터가 필요해집니다.

📒 훈련 데이터

모델이 배우는 데이터. 여러 번 반복해 써도 된다.

🔧 검증 데이터

k 같은 손잡이를 돌려 보는 데이터. 여기서 여러 번 재고 비교해도 괜찮다.

🔒 테스트 데이터

성적표를 적을 때 딱 한 번 쓰는 데이터. 여기 점수를 보고 무언가를 바꿨다면, 그 순간 테스트가 아니게 된다.

조정에는 검증을, 마지막 보고에는 테스트를. 이것이 규칙입니다. 우리 실습은 60명뿐이라 검증까지 따로 떼면 각 몫이 너무 작아집니다. 그래서 다른 길을 씁니다.

교차 검증 — 다섯 번 갈라 평균을 본다

데이터가 적을 때 쓰는 방법이 교차 검증(cross-validation)입니다. 60명을 12명씩 다섯 조각으로 나누고, 조각 하나씩 차례로 채점용으로 씁니다. 다섯 번 재서 평균을 보는 거예요. 모든 사람이 정확히 한 번씩 채점 대상이 됩니다.

회차 60명을 12명씩 다섯 조각으로 이 회차 점수 12345 66.7% 58.3% 66.7% 83.3% 75.0% 평균 70.0% 채점용 조각 학습용 조각 네 개
5겹 교차 검증. 회차마다 파란 조각만 바뀐다. 그림의 다섯 점수는 실습 코드가 k=7에서 실제로 낸 값이다. 같은 k인데 58.3%부터 83.3%까지 벌어지는 것을 보라 — 12명으로 채점하면 한 명이 8.3%p다.

다섯 조각을 번갈아 쓰면 k마다 다섯 개의 점수가 나옵니다. 실습 코드가 낸 결과입니다.

k1회차2회차3회차4회차5회차평균
183.375.050.066.766.768.3%
358.366.766.766.775.066.7%
558.358.358.366.775.063.3%
766.758.366.783.375.070.0%
966.758.366.7100.075.073.3%
1575.058.358.366.783.368.3%
2583.366.775.075.075.075.0%
4566.758.350.075.058.361.7%
5겹 교차 검증(실습 코드가 재는 여덟 개 k). 평균이 가장 높은 것은 k=25의 75.0%다. k=9의 4회차가 100.0%인 것에 속지 말 것 — 12명을 다 맞힌 것뿐이다.
⚠️ 3절과 답이 다르다 — 이것이 오늘의 결론이다

3절의 한 번짜리 분할은 k=3을 골랐습니다. 같은 60명을 5겹 교차 검증으로 재니 k=25가 1등입니다. 어느 쪽이 맞을까요?

"60명으로는 k를 정할 수 없다"가 정직한 답입니다. 어느 쪽도 확실하지 않아요. 다만 다섯 번의 평균은 한 번의 운보다 믿을 만합니다. 이것이 교차 검증을 쓰는 이유입니다.

이 어긋남은 사고가 아니라 이 차시가 가르치려는 바로 그 현상입니다. "한 번의 분할로 고른 k를 믿지 말라"는 문장을, 숫자가 스스로 증명한 셈이지요.

5

과적합을 줄이는 손잡이 넷 — 그리고 예상이 빗나가는 곳

과적합을 줄이는 방법으로 흔히 넷을 듭니다.

📈 데이터를 더 모은다

외울 것이 많아지면 외우기 어려워진다. 가장 널리 권해지는 조언이다.

🌫️ 모델을 단순하게 한다

k-NN이라면 k를 키운다. 신경망이라면 층과 노드를 줄인다.

⏱️ 적당할 때 멈춘다

검증 정확도가 내려가기 시작하면 학습을 멈춘다. 9차시(손실이 줄어드는 쪽으로 조금씩 내려가던 경사하강법)의 반복 횟수가 그 손잡이다. 여기서 테스트를 보면 안 된다 — 4절이 말한 바로 그 실수가 된다.

🧹 잡음을 다룬다

잘못 적힌 기록을 고치거나, 뜻 없는 특성을 뺀다. 4차시(빠진 값과 튀는 값)·6차시(쓸모없는 특성 골라내기)가 하던 일이다.

여기까지는 어느 책에나 있는 목록입니다. 그런데 우리 데이터에서 실제로 재 보면 예상이 하나 빗나갑니다. 직접 확인해 봅시다.

손잡이 ① 잡음을 없애면 — 격차가 6분의 1이 된다

같은 60명, 같은 좌표. 이름표를 뒤집는 비율만 0.15에서 0으로 바꿉니다. 한 번의 분할은 운에 흔들리므로 여기서부터는 분할을 20번 바꿔 평균을 냅니다.

잡음k훈련테스트격차
15%1100.0%64.7%35.3%p
15%386.3%71.0%15.3%p
15%579.7%72.7%7.0%p
15%978.1%76.0%2.1%p
0%1100.0%94.0%6.0%p
0%398.0%90.0%8.0%p
0%595.8%89.7%6.1%p
0%992.1%88.0%4.1%p
분할 20회 평균. 앞 3절의 표(한 번짜리 분할)와 숫자가 조금 다른 것은 그 때문이다.

읽을 것이 셋 있습니다.

  • k=1의 격차가 35.3%p에서 6.0%p로, 6분의 1이 되었습니다. 모델도 코드도 안 바꿨어요. 데이터의 잡음만 뺐습니다. 그러니 격차를 만든 것의 대부분은 모델이 아니라 데이터였습니다.
  • 테스트 정확도가 64.7%에서 94.0%로 뛰었습니다. 잡음 15%가 성능에 씌워 놓은 천장이 그만큼 낮았던 것입니다.
  • 잡음이 없으면 k=1이 모든 k 가운데 가장 좋습니다(94.0 > 90.0 > 89.7 > 88.0). 3절의 경고가 여기서 증명됩니다 — "작은 k가 나쁘다"가 아니라 "잡음이 있을 때 작은 k가 나쁘다"가 정확한 문장입니다.

그리고 잡음이 0이어도 k=1의 훈련 정확도는 여전히 100.0%입니다. 2절에서 본 그 이유 그대로예요 — 자기 자신이 자기의 1등 이웃이니까요.

손잡이 ② 데이터를 더 모으면 — 여기서 예상이 빗나간다

"데이터를 더 모으면 과적합이 줄어든다." 널리 알려진 조언이지요. 정말 그런지 20명 · 40명 · 80명 · 160명으로 재 보았습니다. 160명은 그 60명 뒤에 100명을 더 붙인 것이고, 20명은 그중 앞 20명입니다(같은 규칙·같은 난수열).

잡음k20명40명80명160명추세
15%139.034.036.037.0줄지 않는다
15%515.75.210.810.5들쭉날쭉
0%112.09.59.05.4착실히 준다
0%54.08.85.22.5대체로 준다
칸의 숫자는 훈련 정확도 − 테스트 정확도(격차, %p). 분할 20회 평균.

잡음이 15%일 때는 데이터를 여덟 배로 늘려도 격차가 줄지 않습니다. 39.0 → 34.0 → 36.0 → 37.0. 오히려 20명일 때와 160명일 때가 비슷해요.

왜 그럴까요? 답은 이미 2절에 있습니다. k=1의 훈련 정확도는 데이터 크기와 무관하게 언제나 100.0%로 못 박혀 있습니다. 격차의 위쪽 끝이 고정되어 있으니, 격차가 줄려면 테스트 정확도가 100%에 가까워져야 합니다. 그런데 잡음 15%가 테스트 정확도에 천장을 씌워 두었어요. 이 벽은 데이터를 아무리 모아도 안 무너집니다.

그렇다고 데이터를 모으는 것이 헛일일까요? 아닙니다. 테스트 정확도 자체는 오릅니다(잡음 0 · k=1: 88.0% → 90.5% → 91.0% → 94.6%).

💡 문장을 고쳐 쓰자

"데이터를 더 모으면 격차가 준다"가 아니라 "데이터를 더 모으면 실력(테스트 정확도)이 오른다. 격차를 줄이는 것은 잡음을 다루거나 모델을 단순하게 하는 쪽이다"가 정확합니다. 널리 알려진 조언도, 내 데이터에서 직접 재 보면 조건이 붙습니다. 이것이 오늘 시간에 실제로 재 보는 이유예요.

끝까지 밀어 보기 — 배울 것이 하나도 없다면

잡음을 0.5로 두면 이름표가 사실상 동전 던지기가 됩니다. 배울 무늬가 없어요. 그런 데이터에서도 k=1은 훈련 정확도 100.0%를 받습니다.

k훈련테스트읽는 법
1100.0%46.7%100점인데 동전보다 못하다
382.2%40.0%
964.4%40.0%
4557.8%60.0%아무것도 안 배우는 쪽이 1등
잡음 0.5(이름표가 무작위), 기본 분할. 배울 것이 없는 데이터에서도 훈련 정확도는 100점이 나온다 — 훈련 정확도 100%가 아무 정보도 아니라는 가장 센 증거다. 그리고 이럴 때는 다수결만 하는 k=45가 가장 낫다.
💻

손으로 — 두 곡선을 직접 벌려 보고, 다시 붙여 보기

지금까지 본 표들은 전부 여러분이 오늘 직접 만들어 낼 숫자입니다. 먼저 아래 실험실에서 손잡이를 돌려 감을 잡고, 그다음 파이썬으로 같은 값을 다시 뽑습니다. 둘이 같은 값을 내는지 반드시 대조하세요 — 실험실의 계산기와 실습 코드는 같은 난수열·같은 다수결·같은 동점 처리를 쓰도록 맞춰 두었습니다.

📉 두 곡선 실험실 — 훈련 정확도와 테스트 정확도 INTERACTIVE

가로축은 k, 세로축은 정확도입니다. 노란 선이 훈련 정확도, 하늘색 선이 테스트 정확도예요. 데이터 크기 · 잡음 · 분할을 바꾸면 그 자리에서 k-NN을 다시 돌려 두 곡선을 새로 그립니다. 아래쪽 네모는 지금 고른 k가 그리는 경계선입니다 — 평면의 모든 자리에 대해 "여기에 사람이 오면 합격이라 답할까"를 칠한 그림이에요.

훈련 정확도 테스트 정확도 5겹 교차 검증 두 곡선의 격차 기준선(늘 한쪽만 답하는 모델)
60명 15%
기본 1
훈련 / 테스트45 / 15
잘못 적힌 기록9 / 1
k=1 훈련100.0%
k=1 테스트60.0%
테스트 최고 k3
그때 테스트66.7%
기준선(테스트)46.7%
지금 k의 격차40.0%p
[안내] 본문과 같은 설정으로 시작합니다 — 60명 · 잡음 15% · 기본 분할.

※ 이 실험실은 파이썬의 random과 같은 난수 발생기를 브라우저에서 다시 짠 것입니다. 같은 씨앗이면 같은 60명이 나오고, 다수결의 동점 처리(가장 가까운 이웃이 이긴다)까지 실습 코드와 맞춰 두었습니다. 그래서 화면의 숫자와 실습 코드의 숫자가 소수점 첫째 자리까지 같습니다. 다만 실험실은 실습 코드보다 k를 촘촘히 재기 때문에, 실습 코드가 보지 않는 k가 1등에 끼기도 합니다 — 교차 검증에서 k=11 이 k=25 와 나란히 75.0% 로 공동 1등이 되는 식입니다. 1등이 여럿이면 가운뎃점으로 이어 적습니다. 테스트가 15명뿐이라 동점이 자주 납니다.

과제 ① 표를 채운다 — 분할만 바꿔 본다

데이터 크기 60명 · 잡음 15%로 두고, 분할만 바꿉니다. 네 번 돌려 아래 칸을 공책에 옮겨 적으세요. 확인 문제에서 다시 묻습니다.

분할k=1 테스트k=3 테스트테스트 최고 k그때 테스트
기본
씨앗 4
씨앗 7
씨앗 9

'테스트 최고 k' 칸에 값이 여러 개 나올 수 있습니다. 테스트가 15명뿐이라 같은 점수를 받는 k가 여럿이기 때문이에요. 나온 대로 다 적으세요. 씨앗 4에서는 여섯 개나 공동 1등이 됩니다 — 그 자체가 "한 번의 표로는 k를 못 고른다"의 증거입니다.

'훈련 정확도' 칸이 없는 것을 눈치챘나요? k=1의 훈련 정확도는 네 줄 모두 100.0%라 적을 값이 없습니다. 적을 것이 없는 칸은 정보가 없는 칸입니다.

과제 ② 반례를 만든다 — "k=1이 가장 좋은 설정"을 찾아라

본문은 "k=1은 잡음을 외운다"고 했습니다. 이제 그 말이 무너지는 설정을 여러분이 직접 만들어 보세요.

  1. 잡음을 0%로 내리고 데이터 크기를 160명으로 둔다. 테스트 최고 k가 무엇이 되는지 본다.
  2. 잡음을 0% → 5% → 10% → 15%로 한 칸씩 올린다. k=1이 1등 자리를 내주는 첫 지점을 찾아 공책에 적는다.
  3. 그 지점에서 🙈 자기 자신 빼기를 눌러 본다. 노란 선(훈련)이 어디까지 내려앉는가?

여기서 얻는 문장은 이것입니다 — "k=1이 나쁘다"가 아니라 "잡음이 있을 때 k=1이 나쁘다." 조건 없이 외운 규칙은 다음 데이터에서 반드시 배신합니다.

과제 ③ 경계선을 눈으로 본다

아래쪽 네모(경계선 그림)를 보면서 지금 볼 k 슬라이더를 1 → 3 → 9 → 25 → 45로 올려 보세요.

  • k=1: 하얀 테두리가 쳐진 점(잘못 적힌 기록) 둘레에 작은 섬이 생깁니다. 모델이 그 한 사람을 통째로 외운 자국이에요.
  • k=9 즈음: 섬이 녹고 경계가 대각선 하나에 가까워집니다. 이 데이터를 만든 진짜 규칙이 a + b > 10이니, 그 선이 맞습니다.
  • k=45: 네모 전체가 한 색이 됩니다. 누구를 물어도 같은 답 — 기준선 모델입니다.

섬을 세어 보세요. 섬은 잘못 적힌 기록이 남긴 자국입니다. 다만 섬의 수와 잘못 적힌 기록의 수가 딱 맞지는 않아요 — 진짜 경계선(하얀 점선) 가까이에 있는 잘못된 기록은 같은 색 큰 덩이에 그대로 붙어 버려 섬으로 보이지 않습니다. 기본 분할의 훈련 45명 중 잘못 적힌 기록은 9명인데, 눈에 띄는 섬은 그보다 적습니다.

이제 잡음을 0%로 내려 보세요. 섬이 하나도 남지 않고 경계선이 하얀 점선과 거의 포개집니다. "k=1이 나쁘다"가 아니라 "잡음이 있을 때 k=1이 나쁘다"는 문장이 화면에 그대로 보이는 자리입니다.

파이썬으로 같은 값을 다시 뽑는다

이제 실험실이 아니라 여러분의 코드로 같은 숫자를 만듭니다. 맨 위 블록은 8차시에서 만든 k-NN 그대로입니다 — dist · nearest · knn · accuracy 네 함수를 한 글자도 고치지 않고 다시 실었어요. 브라우저의 파이썬은 쪽이 바뀔 때마다 새로 켜지므로 8차시에서 짠 것을 그냥 부를 수는 없습니다.

여러분이 채울 빈칸은 세 곳입니다.

  • 빈칸 ① train_accuracy(learn, k)의 몸통 — 배운 것과 채점받는 것이 같다는 말을 accuracy( , , k)의 두 자리에 옮겨 적으면 됩니다.
  • 빈칸 ② test_accuracy(learn, check, k)의 몸통 — 배운 것은 learn, 채점받는 것은 check입니다.
  • 빈칸 ③ 자기 자신만 빼고 나머지 훈련 데이터를 모으는 자리. 목록은 +로 이어 붙일 수 있습니다.
⚠️ 빈칸 ③에서 틀렸는데 정상으로 보이는 자리

train_set[:i] + train_set[i:]라고 쓰면 어떻게 될까요? 자기 자신이 그대로 남습니다. 그러면 결과가 100.0%로 나오고, 아무 오류도 나지 않습니다. 잘라 낸 두 조각을 이었는데 원래 목록 그대로인 거예요. 빼야 할 것은 i번째 하나입니다.

출력의 【3】 표가 3절의 표와 한 자리도 다르지 않아야 합니다. 다르다면 빈칸을 다시 보세요. 특히 【2】의 마지막 네 줄 — 100.0 → 64.4, 84.4 → 62.2, 75.6 → 66.7, 75.6 → 73.3 — 을 공책에 옮겨 적으세요. 확인 문제 2번에서 이 숫자를 씁니다.

여기까지가 오늘의 필수입니다. 아래 상자는 네 가지 실험을 더 해 봅니다.

이어서실습 ② — 네 가지 실험 (앞 상자를 스스로 채운 뒤에 여세요)

이 상자에는 빈칸 ①②의 답이 이미 적혀 있습니다. 앞 상자를 먼저 스스로 채워 보고 여세요. 새로 채울 빈칸은 두 곳입니다.

  • 빈칸 ④ 75% 지점에서 자르는 자리. len(shuffled) * 0.75는 45가 아니라 45.0 — 소수입니다. 목록을 자르는 자리에는 정수만 들어갈 수 있어서, 그대로 쓰면 오류가 나요. 소수점을 버려 정수로 만들어야 합니다.
  • 빈칸 ⑤ 교차 검증에서 이번 회차의 조각만 빼고 나머지 넷을 합치는 자리. if j != i를 빠뜨리면 채점용 조각으로 학습하게 되어 모든 k의 평균이 확 오릅니다. 좋아 보이지만 그것이 데이터 누수예요.

이 상자가 뽑는 것이 4절과 5절의 표 전부입니다. 실험이 많아 몇 초 걸립니다.

【5】의 첫 줄에 True가 찍히는 것을 확인하세요. make_data(60, 0.15, 5)가 만든 60명이 【1】의 그 60명과 정확히 같은 사람들이라는 뜻입니다. 그래서 "160명은 그 60명 뒤에 100명을 더 붙인 것"이라는 말이 참이 됩니다. 데이터를 바꿔 놓고 "데이터를 늘렸다"고 말하면 비교가 성립하지 않아요.

💡 더 부숴 볼 것
  • random.seed(5)의 5를 3으로 바꿔 보세요. k=1의 테스트 정확도가 93.3%가 되어 k=3(80.0%)을 이깁니다. 그래도 k=1의 훈련 정확도는 100.0%입니다.
  • data[:45], data[45:]를 data[:54], data[54:]로 바꿔 보세요(90% 분할). 점수가 오르지만 테스트가 6명뿐이라 눈금이 거칠어진 것입니다.
  • 잡음 0.15를 0.5로 바꿔 보세요. 이름표가 거의 무작위인데도 k=1의 훈련 정확도는 100.0%입니다. 그리고 k=45가 테스트 60.0%로 1등이 됩니다.
📖

정리 — 오늘 손에 쥔 것

오늘 우리는 새 알고리즘을 하나도 배우지 않았습니다. 8차시의 k-NN을 글자 하나 안 고치고 그대로 썼어요. 바꾼 것은 무엇으로 채점하느냐 하나뿐인데, 같은 모델의 점수가 100.0%와 60.0% 사이를 오갔습니다.

물음오늘의 답근거가 된 숫자
훈련 정확도 100%는 좋은 소식인가 아니다. k=1이면 구조상 늘 100%다 자기 자신을 빼면 64.4%
모델의 진짜 실력은 무엇으로 재나 학습에 한 번도 안 쓴 테스트 데이터로 k=1 테스트 60.0%
복잡도를 올리면 어떻게 되나 훈련은 계속 오르고 테스트는 정점 뒤 내려온다 격차 17.8 → 40.0%p
한 번의 분할을 믿어도 되나 안 된다. 가르는 방법만 바꿔도 답이 흔들린다 흔들림 폭 20.0%p
테스트 점수를 보며 k를 고르면 성적이 부풀려진다. 그래서 검증 데이터가 따로 필요하다 +11.7%p
데이터를 더 모으면 과적합이 주나 잡음이 있으면 격차는 안 준다. 대신 실력이 오른다 격차 39.0 → 37.0%p
테스트 88.0 → 94.6%

가장 오래 남았으면 하는 문장은 이것입니다.

📖 오늘의 한 줄

흔들리지 않는 숫자는 정보를 담고 있지 않다. k=1의 훈련 정확도 100.0%가 그렇다 — 데이터를 바꿔도, 크기를 여덟 배로 늘려도, 이름표를 동전 던지기로 만들어도 100.0%다. 구조가 정해 버린 값이기 때문이다.

다음 시간에는 정확도라는 숫자 하나로는 부족한 자리를 봅니다. 환자가 1,000명 중 20명뿐인 검사에서 "모두 정상"이라고만 답하면 정확도가 98.0%가 나와요. 숫자는 훌륭한데 환자를 한 명도 못 찾습니다. 12차시에서 정확도를 네 칸(혼동행렬)으로 쪼개 그 함정을 다룹니다. 그리고 오늘 만든 훈련/테스트 가르기는 15차시에서 실제 급식 잔반 240일치에 그대로 쓰입니다 — 비율만 70/30으로 달라집니다(60~80% 안이면 됩니다).

✅

확인 문제

✍️ 문제마다 답을 쓰고 제출하기를 누르세요. 제출하면 모범 답안이 열리고, 제출한 답은 선생님께 전달됩니다.

1. 훈련 데이터 · 검증 데이터 · 테스트 데이터의 역할을 각각 한 줄로 쓰시오. 그리고 테스트 데이터는 몇 번 쓰는가?
📖 모범 답안

훈련 데이터 — 모델이 보고 배우는 데이터. 반복해 써도 된다.
검증 데이터 — k처럼 사람이 정하는 손잡이를 돌려 보고 비교하는 데이터. 여기서는 여러 번 재도 된다.
테스트 데이터 — 성적표에 적을 점수를 내는 데이터. 학습에도, 손잡이 조정에도 쓰지 않는다.

테스트는 맨 마지막에 딱 한 번 쓴다. 까닭: 테스트 점수를 보고 무언가를 바꾸는 순간 그 데이터는 이미 '처음 보는 데이터'가 아니게 되어, 성적이 부풀려진다. 실제로 재 보면 분할 12개 평균에서 11.7%p가 부풀려졌다.

2. 오늘 실습에서 k=1의 훈련 정확도가 100.0%였다. 그 까닭을 설명하고, 실습 코드가 '자기 자신을 빼고' 다시 잰 네 값 (k = 1 · 3 · 5 · 9)을 쓰시오. 그 네 값이 왜 그런 모양인지도 한 줄 덧붙이시오.
📖 모범 답안

까닭: 훈련 데이터에 있는 사람을 다시 물어보면, 거리를 재는 줄에 자기 자신도 서 있다. 자기와 자기 사이의 거리는 0이라 1등 이웃은 언제나 자기 자신이다. k=1은 그 하나만 보므로 자기 이름표를 그대로 돌려준다. 실습 출력에서 세 사람 모두 1등 이웃까지의 거리가 0.000으로 찍힌 것이 그 증거다.

자기 자신을 뺀 값: k=1 → 64.4%, k=3 → 62.2%, k=5 → 66.7%, k=9 → 73.3%. (그냥 잰 값은 각각 100.0 / 84.4 / 75.6 / 75.6%)

모양의 까닭: 두 값의 차이가 35.6 → 22.2 → 8.9 → 2.3%p로 줄어든다. 자기 표 한 장이 다수결에서 차지하는 무게가 1/k이므로, k가 커질수록 자기 표가 결과를 뒤집기 어려워지기 때문이다.

3. 오늘 뽑은 k별 표(훈련 45 / 테스트 15)에서 과적합 구간과 과소적합 구간을 각각 지목하고, 그렇게 판단한 근거를 쓰시오. 그리고 이 표만 보고 k를 고른다면 몇을 고르겠는가? 그 선택을 얼마나 믿을 수 있는지도 함께 쓰시오.
📖 모범 답안

과적합 구간: k=1. 근거 — 훈련 100.0%인데 테스트가 60.0%로 격차가 40.0%p다. k=3(17.8%p)에서 k=1로 내려가는 순간 격차가 두 배 넘게 벌어진다. 훈련만 오르고 테스트는 오히려 떨어지는 것이 과적합의 정의다.

과소적합 구간: k=21 이상. 근거 — 여기서부터 훈련도 테스트도 함께 내려간다 (75.6/46.7 → 68.9/46.7 → 66.7/46.7). k=45는 훈련 데이터 45명 전부가 투표하므로 누구를 물어도 다수결만 답하는 모델이고, 그 점수 훈련 66.7% · 테스트 46.7%가 기준선이다.

고른다면 k=3(테스트 66.7%). 다만 거의 믿을 수 없다. 테스트가 15명뿐이라 한 명이 6.7%p이고, k=3과 k=5의 차이(66.7% vs 60.0%)는 한 명 차이다. 실제로 분할을 12번 바꿔 보면 k=3의 점수가 60.0~80.0%로 20.0%p 흔들리고, 같은 60명을 5겹 교차 검증으로 재면 k=25가 1등이 된다.

4. 어떤 학생이 k를 1부터 45까지 다 돌려 보고 테스트 정확도가 가장 높은 k를 고른 뒤, 보고서에 "이 모델의 정확도는 93.3%"라고 적었다. 무엇이 잘못되었는가? 어떻게 고쳐야 하는가?
📖 모범 답안

잘못된 점: 테스트 데이터를 k를 고르는 데 썼다. 그 순간 테스트는 더 이상 '학습에 한 번도 쓰지 않은 데이터'가 아니다. 사람 손을 거쳐 정보가 새어 들어간 것이고, 보고한 93.3%는 이 모델이 앞으로 만날 새 사람들에게서 기대할 수 있는 점수가 아니다.

얼마나 부풀려지나: 분할 12개로 재 보면 미리 정한 k=3을 쓸 때 평균 73.9%인데, '그 분할의 최고 k'를 골라 보고하면 85.6%가 된다 — 11.7%p 부풀려진다. 모델을 조금도 좋게 만들지 않고 얻은 점수다.

고치는 법: 데이터를 셋으로 가른다. 검증 데이터에서 k를 고르고, 테스트는 고른 뒤에 딱 한 번 연다. 데이터가 적어 셋으로 못 가르면 교차 검증으로 k를 고르고, 테스트는 여전히 마지막에 한 번만 쓴다.

5. 실험실 과제 ①에서 적은 네 줄 (기본 · 씨앗 4 · 씨앗 7 · 씨앗 9)의 '테스트 최고 k' 칸을 먼저 옮겨 적으시오. 그리고 어떤 모둠이 학교에서 데이터를 30개밖에 못 모아, 가르는 방법에 따라 결과가 너무 달라져 어느 설정을 골라야 할지 알 수 없다고 한다. 그 네 줄을 근거로 어떤 방법을 권하겠는가? 그 방법이 구체적으로 무슨 일을 하는지도 설명하시오.
📖 모범 답안

과제 ① 네 줄(60명 · 잡음 15%, 분할만 바꿈)

분할k=1 테스트k=3 테스트테스트 최고 k그때 테스트
기본60.0%66.7%366.7%
씨앗 473.3%60.0%1 · 5 · 11 · 15 · 21 · 3173.3%
씨앗 773.3%66.7%9 · 1193.3%
씨앗 973.3%80.0%1593.3%
네 줄 모두 k=1의 훈련 정확도는 100.0%였다 — 그래서 그 칸은 아예 두지 않았다. '최고 k'는 한 번도 같은 값이 아니었고, 씨앗 4에서는 여섯 개가 공동 1등이었다.

이 네 줄이 곧 근거다. 데이터도 코드도 그대로인데 가르는 방법만 바꿨더니 '가장 좋은 k'가 3 → 1·5·11·15·21·31 → 9·11 → 15로 매번 달라졌다. 한 줄만 보고 k를 골랐다면 네 번 중 세 번은 다른 답을 골랐을 것이다.

그러니 교차 검증(cross-validation)을 권한다. 5겹이라면 이렇게 한다 — 30개를 6개씩 다섯 조각으로 나누고, 조각 하나를 채점용으로 빼고 나머지 넷으로 학습해 점수를 낸다. 이것을 조각을 바꿔 가며 다섯 번 반복하고 다섯 점수의 평균을 본다. 모든 데이터가 정확히 한 번씩 채점 대상이 되므로, 한 번의 운 나쁜 분할에 속지 않는다.

왜 필요한가: 오늘 60명으로 확인했듯, 가르는 방법만 바꿔도 k=3의 점수가 60.0%에서 80.0%까지 20.0%p 흔들렸고, '가장 좋은 k'도 12번 중 여섯 가지가 나왔다. 30개면 흔들림이 더 크다.

주의: 교차 검증은 k를 고르는 데 쓰는 도구다. 최종 성적표는 여전히 따로 떼어 둔 테스트 데이터로 적어야 한다.

6. 과적합을 줄이는 방법을 세 가지 쓰시오. 그리고 오늘 쓴 60명 데이터(잡음 15%, k=1)에서는 어느 것이 가장 효과가 클지 먼저 예상해 보고, 실습 결과와 맞는지 확인하시오.
📖 모범 답안

세 가지(예): ① 데이터를 더 모은다 ② 모델을 단순하게 한다(k를 키운다) ③ 잡음을 다룬다(잘못 적힌 기록을 고치거나 뜻 없는 특성을 뺀다). 여기에 ④ 적당할 때 학습을 멈춘다를 더할 수 있다.

많은 사람이 ①을 고른다. 이 데이터에서는 틀렸다. 20 → 40 → 80 → 160명으로 여덟 배 늘려도 k=1의 격차는 39.0 → 34.0 → 36.0 → 37.0%p로 줄지 않는다. k=1의 훈련 정확도가 데이터 크기와 무관하게 언제나 100.0%로 못 박혀 있고, 테스트 정확도는 잡음 15%가 씌운 천장 위로 못 올라가기 때문이다.

실제로 효과가 큰 것은 ③과 ②다. 잡음을 0으로 하면 k=1의 격차가 35.3%p → 6.0%p로 6분의 1이 된다. k를 키우면 같은 잡음에서도 35.3 → 15.3 → 7.0 → 2.1%p(k=1,3,5,9)로 준다.

그렇다고 ①이 쓸모없는 것은 아니다. 데이터를 늘리면 격차는 안 줄어도 실력은 오른다 — 잡음 0·k=1의 테스트 정확도가 88.0 → 90.5 → 91.0 → 94.6%로 오른다. "데이터를 더 모으라"는 조언은 격차를 줄이는 조언이 아니라 실력을 올리는 조언이다.

🔁 되돌아보기

오늘 8차시의 k-NN을 한 글자도 고치지 않고, 채점하는 법만 바꿔 같은 모델의 점수가 100.0%와 60.0% 사이를 오가는 것을 직접 재 보았다. 다음 시간에는 그 '정확도'라는 숫자 하나마저 믿을 수 없는 자리를 만난다 — 환자가 2%뿐인 검사에서 아무 일도 안 하는 모델이 98.0%를 받는 곳이다.

🔎

더 알아보기

채점하는 법을 바꾸자 진짜 실력이 드러난 사건들, 그리고 두 곡선 뒤에 숨은 원리

벽돌 건물 앞마당에서 모자를 쓴 사람이 상자를 들어 말에게 보이고 있다. 둘 사이의 탁자 위에는 작은 막대들이 세워져 있다
역사

셈하는 말 '영리한 한스' — 채점하는 법을 바꾸자 드러난 것

20세기 초 독일 베를린에 영리한 한스(Clever Hans)라는 말이 있었습니다. 주인 빌헬름 폰 오스텐이 덧셈 같은 문제를 내면 한스는 발굽을 두드린 횟수로 답했고, 맞히는 일이 많아 큰 화제가 되었어요. 1904년에는 심리학자 카를 슈툼프가 참여한 조사단이 살펴보았지만 속임수를 찾지 못했습니다.

1907년, 슈툼프의 조수 오스카 풍스트는 채점하는 법을 바꿨습니다. 묻는 사람이 답을 모르게 하거나, 한스가 묻는 사람을 볼 수 없게 한 것이지요. 그러자 정답률이 크게 떨어졌습니다. 한스는 셈을 한 것이 아니라, 두드린 수가 정답에 이르면 사람들이 저도 모르게 긴장을 푸는 작은 몸짓을 읽고 멈췄던 것입니다.

오늘 수업과 뼈대가 같습니다. 답을 아는 사람 앞에서 잰 점수는 훈련 데이터로 잰 정확도처럼 부풀려져 있었고, 답이 새어 나갈 길을 막고 다시 재자 진짜 실력이 드러났어요. 그래서 기계학습에서는 모델이 과제 대신 엉뚱한 단서로 맞히는 일을 영리한 한스 효과라고 부릅니다. 말 사진을 말의 생김새가 아니라 사진 귀퉁이의 출처 표시로 알아보던 분류기가 실제로 보고되기도 했습니다.

사진: '한스가 덧셈을 배운다'라는 제목의 옛 사진 · 출처: Karl Krall, Wikimedia Commons (Public domain)

모델을 고친다 테스트에 제출 🔒 점수를 본다 이 고리를 여러 번 돌수록 테스트가 새어 나간다 오늘 4절에서 잰 같은 효과 (분할 12개 평균) 미리 정한 k=3 73.9% 테스트 보고 고른 k 85.6% +11.7%p — 모델은 그대로 막대 길이는 0%부터 잰 비율 그대로다.
현장

시험지를 몰래 여러 번 본 대회 — 테스트 점수가 새어 나가는 길

이미지 인식 대회 ILSVRC(이미지넷 대회)는 참가 팀에게 테스트 이미지의 정답을 주지 않았습니다. 팀은 예측을 서버에 올리고 점수만 받았고, 제출은 일주일에 두 번으로 묶여 있었어요. 4절에서 본 대로, 테스트 점수를 자꾸 보며 손잡이를 돌리면 그 점수가 부풀려지기 때문입니다.

2015년 6월 주최 측은 바이두의 한 팀이 2014년 11월부터 2015년 5월 사이 30개가 넘는 계정으로 200번 넘게 제출했다고 발표하고, 그 팀의 참가를 12개월 동안 막았습니다. 회사는 공개 사과했어요. 제출 한 번 한 번은 '점수만 본' 것이지만, 그 점수를 보고 고치기를 되풀이하면 그림의 고리처럼 테스트 데이터가 조금씩 학습 속으로 새어 들어갑니다.

그래서 캐글(Kaggle) 같은 경진 대회는 테스트 데이터를 둘로 나눠, 대회 중에는 공개 순위표용 일부의 점수만 보여 주고 나머지(비공개 순위표)는 대회가 끝난 뒤에 엽니다. 공개 순위표에 맞춰 여러 번 고친 팀이 마지막에 순위가 크게 밀리는 일이 드물지 않아요. 오늘 표의 +11.7%p가 대회판에서 벌어지는 모습입니다.

k=1 — 분산이 크다 k=45 — 편향이 크다 분할마다 답이 크게 달라진다 평균은 가운데 근처 분할을 바꿔도 거의 같은 답 그러나 가운데를 벗어난다 가운데 = 진짜 규칙 · 점 하나 = 분할 하나로 만든 모델
원리 더 깊이

편향과 분산 — 두 곡선이 갈라지는 까닭을 두 낱말로

4절에서 60명을 12번 다르게 갈랐지요. 분할마다 모델이 하나씩 생기니 모델 12개가 생긴 셈입니다. 이 모델들을 과녁에 쏜 화살로 생각해 봅시다. 과녁 가운데는 데이터를 만든 진짜 규칙 a + b > 10입니다.

k=1은 분할마다 경계선이 크게 달라집니다. 실험실에서 본 작은 섬들이 분할마다 다른 자리에 생기거든요. 화살이 가운데 둘레에 넓게 흩어지는 이 성질을 분산(variance)이라 합니다. k=45는 거의 늘 다수 쪽 하나만 답하니 분할을 바꿔도 모델이 변하지 않지만, 가운데에서 멀리 벗어나 있어요. 이렇게 한쪽으로 치우친 성질을 편향(bias)이라 합니다. 과적합은 분산이 큰 쪽, 과소적합은 편향이 큰 쪽이고, k를 키우면 분산을 줄이는 대신 편향을 떠안습니다.

회귀에서 제곱 오차로 재면 새 데이터에서의 평균 오차가 (편향)² + 분산 + 줄일 수 없는 잡음으로 정확히 나뉩니다. 마지막 항이 5절에서 본 잡음 15%가 씌운 천장에 해당해요. 어떤 k를 골라도 이 항은 줄지 않고, 잘못 적힌 기록을 바로잡는 일만이 그 천장을 올립니다. 잡음을 0으로 두자 k=1의 테스트 정확도가 94.0%로 뛴 것이 그 증거입니다.