단원 홈
4단원 · 6차시

보여 주기 전에
다듬고, 섞고, 갈라 놓기

모으기만 하면 되는 줄 알았습니다. 그런데 표를 열어 보면 빈칸이 있고, 눈에 띄게 튀는 값이 있고, 열마다 단위가 제각각입니다. 오늘 끝에는 훈련 뭉치와 테스트 뭉치가 손에 있어야 합니다. 7·8·9·10차시가 전부 그 두 뭉치 위에서 돕니다.

성취기준 12인기04-03
전처리결측이상치 정규화데이터 누수훈련·테스트 분할
🎯 학습 목표
  • 우리 팀 표에서 빈칸과 튀는 값을 찾아내고, 무엇을 어떻게 처리했는지 까닭과 함께 적을 수 있다.
  • 먼저 섞어 나눈 뒤 훈련 데이터로만 자를 재는 순서를 지켜, 데이터 누수를 막을 수 있다.
  • 정규화한 테스트 값이 0~1을 벗어나는 것이 왜 옳은지 자기 데이터로 확인해 설명할 수 있다.
🤔

여는 장면 — 다 모았는데 왜 아직 못 넣는가

지난 시간에 팀마다 계획서와 플랜 B를 냈습니다. 데이터도 어느 정도 모였습니다. 이제 코드에 집어넣기만 하면 될 것 같습니다. 그런데 그렇지 않습니다.

이 단원이 함께 쓰는 견본 표를 열어 봅시다. 학교 분리수거장에서 찍은 페트병 사진 160장을 사람이 눈으로 재어 적은 표라는 설정입니다. 열은 일곱 개 — 반사도·밝기·투명도·이물질·찌그러짐·촬영조건·재활용입니다. 앞의 다섯이 특성, 촬영조건이 집단, 재활용이 이름표입니다. (지어낸 표입니다. 우리 학교의 실제 기록도, 어느 기관의 공개 자료도 아닙니다.)

표를 죽 훑으면 세 가지가 눈에 걸립니다.

  • 빈칸이 세 칸 있습니다. 반사도 한 칸, 투명도 한 칸, 이물질 한 칸. 측정하다 놓쳤거나 옮겨 적다 빠뜨린 자리입니다. 파이썬은 그 자리에 None이 들어 있고, None은 뺄 수도 나눌 수도 없습니다.
  • 말이 안 되는 값이 두 개 있습니다. 밝기 열에 1210이 하나, 투명도 열에 722.0이 하나. 이 사진기의 밝기는 80~335 사이이고, 투명도는 %니까 0~100입니다.
  • 열마다 단위가 다릅니다. 반사도는 0.43~1.7 사이를 오가는데 밝기는 80~335입니다. 폭의 비가 약 201배입니다. 이대로 거리를 재면 거리 제곱의 90% 넘게(훈련 110개로 재면 91.66%) 밝기 하나가 가져갑니다.

이 셋을 그냥 두고 모델에 넣으면 어떻게 될까요? 첫째는 오류로 멈춥니다. 둘째와 셋째는 멈추지 않습니다 — 그게 더 나쁩니다. 오류 없이 돌면서 조용히 틀린 숫자를 내놓기 때문입니다.

모니터가 줄지어 놓인 긴 책상 앞에서 화면을 보거나 종이 자료를 넘겨 보며 각자 작업하는 학생들
오늘부터 네 차시는 이 장면입니다. 팀마다 진도가 다르고, 화면에 뜨는 표도 다릅니다. 오늘 끝에 모든 팀이 똑같이 손에 쥐어야 하는 것은 딱 하나 — 훈련 뭉치와 테스트 뭉치입니다. 출처: MCPearson, Wikimedia Commons (CC BY-SA 3.0)
📖 오늘 팀이 해야 할 일 하나

우리 팀 표를 다듬고(빈칸·튀는 값) → 섞고 → 갈라 놓아 훈련 뭉치와 테스트 뭉치를 만든다. 그리고 그 두 뭉치를 어떻게 만들었는지 실험 기록 첫 줄에 적는다. 모델은 오늘 안 만듭니다. 모델은 다음 시간입니다.

1

이론은 이미 배웠다 — 오늘은 도구 하나만

오늘 쓰는 개념은 전부 2단원에서 배운 것입니다. 이 단원은 배우는 단원이 아니라 만드는 단원이므로 다시 설명하지 않습니다. 대신 어디로 돌아가면 되는지만 적어 둡니다. 모르겠으면 그 차시를 열어 두고 오늘 실습을 하세요.

오늘 쓰는 것어디서 배웠나Ⅳ-6이 하는 일
빈칸과 튀는 값 다루기 Ⅱ-4 — 키가 1750cm인 학생 우리 표의 빈칸 3칸·튀는 값 2개를 우리가 판정한다
min-max 정규화 Ⅱ-5 — 센티미터와 걸음 수를 나란히 테스트 240칸 중 3칸이 0~1을 벗어나는 것을 확인한다
훈련과 테스트로 나누기 Ⅱ-11 — 외운 것인가 이해한 것인가 158행을 훈련 110 · 테스트 48로 가른다
집단별로 갈라 재기 Ⅱ-3 · Ⅲ-7 오늘은 집단 열을 정해 두기만 한다. 쓰는 것은 Ⅳ-9다
표의 숫자는 모두 견본 데이터를 실제로 돌려 나온 값입니다. 팀 데이터를 넣으면 당연히 달라집니다.

도구는 하나입니다. 다섯 차시가 함께 쓰는 함수 골격입니다. 6·7·8·9·10차시의 파이썬 코드는 전부 같은 함수들로 되어 있고, 차시마다 바뀌는 것은 맨 위 【0】 상자와, 그 아래에서 무엇을 화면에 띄우는가뿐입니다. 오늘 이 골격을 처음 만나고, 다음 네 차시는 그대로 다시 싣습니다.

⚠️ "DATA만 바꾸면 된다"가 아니다

우리 팀 데이터를 넣을 때 갈아 끼우는 것은 【0】 상자 전체입니다. DATA 하나가 아니라 그 위의 다섯 줄 — NAMES · FEATURES · LABEL · GROUP · POSITIVE — 를 함께 고쳐야 합니다.

NAMES를 안 고치면 첫 줄에서 바로 멈춥니다: ValueError: 열 수가 안 맞는 줄 [2] (열 이름은 7개인데 그 줄은 다르다). 반대로 이 오류는 고마운 오류입니다 — 붙여 넣다 쉼표를 하나 빠뜨린 것을 그 자리에서 잡아 주니까요.

빈칸을 무엇으로 메울까요? 이 골격은 그 열의 중앙값으로 메웁니다. 견본에서 반사도는 1.1, 투명도는 50.2, 이물질은 21.2로 메워집니다. 평균으로 메우면 투명도가 56.25가 됩니다 — 아직 안 버린 722.0이 평균을 끌어올린 값입니다. 중앙값은 그 한 칸을 무시합니다. Ⅱ-4에서 배운 그대로입니다.

튀는 값은 누가 정할까요? 골격의 clean()은 기본값이 "표시만"입니다. 평균에서 표준편차 3개 밖에 있는 값을 찾아서 보여 줄 뿐 지우지 않습니다. 견본에서는 두 칸이 걸립니다 — 밝기 1210(z=9.2)과 투명도 722.0(z=11.8). 여기서 사람이 들여다봅니다. 121에 0을 하나 더 쳤고, 72.2의 소수점이 빠진 것이 눈으로 보입니다. 그래서 그 두 줄을 버리기로 사람이 정하고, 그다음에 clean()을 다시 부릅니다.

ℹ️ 왜 두 번 부르나

1750cm는 오타지만 205cm는 진짜 있을 수 있는 키입니다(Ⅱ-4). 코드는 "여기가 이상합니다"까지만 말할 수 있고, "그러니 지웁시다"는 말할 수 없습니다. 표시하는 것과 버리는 것을 한 번으로 줄이면 그 사이에 있어야 할 사람의 판단이 사라집니다. 우리 팀 표에서도 똑같이 하세요 — 먼저 표시하고, 눈으로 보고, 그다음 정합니다.

2

순서가 전부다 — 나누고, 훈련으로만 자를 재고, 그 자로 테스트를 잰다

다듬은 표가 158행 남았습니다. 이제 훈련과 테스트로 나누고 정규화를 합니다. 이 둘의 순서가 오늘의 핵심입니다.

먼저 정규화하고 나누면 어떻게 될까요? 정규화의 자(최솟값·최댓값)를 잴 때 테스트가 될 행들도 함께 들어갑니다. 모델은 아직 테스트를 못 봤다고 해 놓고, 테스트의 최댓값은 이미 자 안에 들어가 있는 셈입니다. 이것을 데이터 누수라고 합니다.

✗ 잘못된 순서 — 자를 먼저 잰다 다듬은 표 158행 전체 158행으로 자 재기 밝기 [80, 335] 그다음 나누기 110 / 48 테스트의 끝값 335가 자 안에 있다 0~1을 벗어나는 칸: 0칸 멈추지 않는다. 오류도 안 난다. ✔ 옳은 순서 — 나누고 나서 훈련으로만 잰다 다듬은 표 158행 훈련 110 테스트 48 훈련 110으로만 자 재기 밝기 [80, 331] 그 자로 테스트를 잰다 테스트는 자에 들어가지 않았다 0~1을 벗어나는 칸: 3칸 벗어나는 것이 정상이다 두 판의 차이는 자 두 줄뿐이다 — 밝기 [80, 335] 대 [80, 331], 이물질 [0.8, 39.6] 대 [0.9, 39.2]. 그런데 그 두 줄이 "이 성적표가 정직한가"를 가른다. 0칸이 나오면 잘한 것이 아니라, 누수를 의심해야 한다.
같은 데이터·같은 코드인데 순서만 다릅니다. 위쪽은 오류 없이 잘 돌아갑니다 — 그래서 위험합니다.

순서 못지않게 중요한 것이 섞기입니다. 모은 순서 그대로 앞에서 70%를 자르면 어떻게 될까요? 팀은 대개 조건별로 몰아서 모읍니다 — 밝은 날 다 찍고, 그다음 흐린 날 다 찍는 식으로요. 그렇게 모은 표를 촬영조건 순으로 정렬해 두고 안 섞은 채 자르면, 테스트 48개가 전부 '어두움'이 됩니다. 훈련은 밝은 사진만 보고, 시험은 어두운 사진만 봅니다.

✗ 안 섞고 잘랐다

촬영조건 순으로 정렬한 표를 그대로 자르면 → 테스트 어두움 48 · 밝음 0.

견본을 그대로 안 섞고 자르면 밝음 30 · 어두움 18이 나와 별일 없어 보입니다. 이 견본이 이미 섞여 있기 때문이지, 안 섞어도 괜찮다는 뜻이 아닙니다.

✔ 섞고 잘랐다 (씨앗 42)

테스트 밝음 28 · 어두움 20. 두 집단이 다 들어 있습니다.

씨앗을 고정하는 까닭은 "42가 좋아서"가 아니라 다시 돌렸을 때 같은 표가 나오게 하려는 것입니다. 적어 두지 않은 씨앗으로 얻은 결과는 다시 만들 수 없습니다.

마지막으로, 나눈 뒤에 크기를 잽니다. 테스트가 4개면 나올 수 있는 정확도가 0 / 25 / 50 / 75 / 100% 다섯 개뿐입니다. 그 숫자로는 "k를 키우면 좋아진다" 같은 말을 할 수 없습니다. 그래서 골격의 check_sizes()가 테스트 20개 이상 · 집단마다 15개 이상인지 돌리기 전에 확인하고, 못 미치면 경고를 찍습니다. 견본은 테스트 48 · 밝음 28 · 어두움 20이라 넉넉히 넘깁니다.

3

Ⅱ-5의 물음이 되돌아온다 — 0~1을 벗어나는 것이 옳다

Ⅱ-5에서 걸음 수와 공부 시간을 0~1로 옮겼을 때, '나'의 좌표가 [0.9677, -0.0417]로 나왔습니다. 0~1로 옮긴다고 해 놓고 음수가 나온 것이지요. 까닭은 이랬습니다 — 내 공부 시간 2.0이 자를 만든 친구 셋의 최솟값 2.1보다 작았기 때문입니다.

오늘 같은 일이 240칸 가운데 3칸에서 납니다. 240은 테스트 48개 × 특성 5개입니다. 어느 칸인지까지 화면이 찍어 줍니다.

테스트 번호특성원래값훈련으로 잰 자정규화 결과
10이물질39.6[0.9, 39.2]1.0104
25밝기335[80, 331]1.0159
37이물질0.8[0.9, 39.2]−0.0026
견본 데이터 · 씨앗 42 · 비율 0.7. 나머지 237칸은 얌전히 0~1 안에 들어옵니다.

테스트 25번의 밝기가 335인데, 훈련 데이터에서 가장 밝았던 사진이 331이었습니다. 훈련보다 밝은 사진이 시험에 나온 것이지요. 그래서 1을 살짝 넘습니다. 고장이 아닙니다. 오히려 이 세 칸이 "테스트를 자에 넣지 않았다"는 증거입니다.

⚠️ 0칸이 나왔다면 잘한 것이 아니다

전체 158행으로 자를 재면 벗어난 칸이 3칸에서 0칸이 됩니다. 숫자만 보면 "깨끗해졌다"로 읽히지요. 정반대입니다. 벗어난 칸이 하나도 없다는 것은 테스트의 끝값이 이미 자 안에 있다는 뜻일 수 있습니다. 그러니 이렇게 읽으세요 — 0칸이 나오면 누수를 의심한다. (테스트가 아주 많거나 훈련이 우연히 양 끝을 다 가지고 있어도 0칸이 나옵니다. 그때는 씨앗을 바꿔 다시 재 보세요.)

여기서 흔한 오해 하나를 미리 끊어 둡시다. "누수를 하면 성능이 부풀려진다"고 단정하지 마세요. min-max 정규화의 누수는 열마다 숫자 둘(최솟값·최댓값)만 새어 나가는, 가장 약한 누수입니다. 견본에서 실제로 재 보면 다음 시간에 만들 모델의 정확도가 거의 오르지 않습니다. 그런데도 하면 안 되는 까닭은 따로 있습니다.

💡 왜 하면 안 되는가

좋아 보이는 것이 문제가 아니라, 그 숫자가 정직한지를 우리가 확인할 수 없게 되는 것이 문제입니다. 이번에는 거의 0이었지만, 이름표가 새거나 같은 사진이 훈련과 테스트에 둘 다 들어가면 정확도가 통째로 거짓말이 됩니다. 그때 가서 "이번 누수는 약한 누수였을까"를 따질 방법이 없습니다. 그래서 순서를 규칙으로 굳혀 두는 것입니다.

💻

손으로 — 우리 표를 훈련 뭉치와 테스트 뭉치로

남은 시간은 전부 여러분 팀의 것입니다. 먼저 팀이 지금 어디에 서 있는지 고르세요. 어느 길로 가도 오늘 끝에는 훈련 뭉치와 테스트 뭉치가 손에 남습니다.

🅰 우리 데이터가 표로 정리돼 있다

바로 활동 ②로 가서 【0】 상자를 우리 것으로 갈아 끼우세요. 그전에 활동 ①을 5분만 돌려 무엇이 나오는지 보고 가면 훨씬 빠릅니다.

🅱 모으는 중이라 아직 반쯤이다

지금 있는 만큼만 표로 만들어 넣어 보세요. 행이 모자라면 check_sizes()가 무엇이 얼마나 모자란지를 숫자로 찍어 줍니다. 그 경고를 보는 것 자체가 오늘의 산출물입니다.

🅲 아직 데이터가 없다

견본 160행을 그대로 씁니다. 코드는 한 글자도 안 고치고 그냥 돌아갑니다. 오늘 할 일은 다 할 수 있고, 팀 데이터는 다음 시간에 갈아 끼우면 됩니다.

활동 ① 관제판 — 설정을 바꾸면 표가 어떻게 달라지나 (8분)

아래 관제판은 파이썬을 부르지 않고 브라우저가 그 자리에서 계산합니다. 섞는 방식까지 파이썬과 똑같이 맞춰 두었으므로, 기본값(중앙값 · z 3.0 · 그 행을 버림 · 훈련만으로 · 씨앗 42 · 0.7)에서 나오는 숫자는 아래 파이썬 실행 결과와 정확히 같습니다. 먼저 그것부터 확인하세요.

🧪 전처리 관제판 — 다듬고, 섞고, 갈라 놓기 INTERACTIVE

견본 160행이 아래 표에 그대로 들어 있습니다. 여섯 가지를 고르면 그 자리에서 다시 계산합니다. 캔버스는 네 단계를 보여 줍니다 — ① 원본 → ② 다듬은 뒤 → ③ 섞어 나눈 뒤 → ④ 자를 맞춘 뒤. ▶ 재생을 누르면 네 단계가 이어서 지나가고, 마지막 단계에서 0~1을 벗어난 칸에 표시가 붙습니다.

작은 값 큰 값 빈칸(None) 튀는 값(z 초과) 메운 칸 훈련 테스트 0~1을 벗어난 칸
남은 행—
훈련—
테스트—
테스트 집단—
0~1 벗어난 칸—
크기 경고—

자 두 벌 견주기 — 전체로 잰 자 / 훈련으로만 잰 자

[안내] 기본값 그대로 ▶ 재생을 눌러 보세요. 158행 · 훈련 110 · 테스트 48 · 벗어난 칸 3칸이 나오면 파이썬과 같은 판입니다.

과제 ① — 표를 채운다

아래 네 설정을 차례로 돌려 공책에 옮겨 적으세요. 칸에 바로 적어도 됩니다. 나머지 설정은 기본값 그대로 두고 한 번에 한 가지만 바꿉니다 — 한 번에 둘을 바꾸면 무엇 때문에 달라졌는지 알 수 없습니다.

바꾼 것남은 행훈련테스트밝음 / 어두움벗어난 칸경고
기본값 그대로
튀는 값 → 표시만 함
정규화 자 → 전체로
훈련 비율 → 0.90

과제 ② — 반례를 만든다

  • 벗어난 칸을 0칸으로 만들어 보세요. 설정 하나만 바꾸면 됩니다. 0칸이 되었을 때 무엇이 없어진 것인지 한 문장으로 적으세요.
  • 크기 경고를 띄워 보세요. 훈련 비율을 0.70에서 0.05씩 올리며, 경고가 처음 뜨는 값과 그때 어느 집단이 몇 개로 줄었는지 적으세요.
  • 씨앗만 바꿔 보세요. 42 · 1 · 2 · 3 · 5 · 7 · 11 · 100 여덟 개를 넣으며 훈련 밝기 범위와 벗어난 칸 수가 어떻게 흔들리는지 보세요. 훈련·테스트 개수는 안 흔들립니다. 왜 그런지 함께 생각해 보세요.
💡 관제판이 답을 대신 채점해 주지 않는 자리

자 두 벌 견주기 표에서 다섯 줄이 모두 "같다"로 나오면 의심하세요. 견본·씨앗 42에서는 두 줄(밝기·이물질)이 갈리는 것이 정상입니다. 다섯 줄이 다 같다면 자를 전체로 쟀거나, 테스트가 너무 작아 끝값을 못 건드린 것입니다.

활동 ② 파이썬 — 빈칸 다섯 곳을 채우고 돌린다 (9분)

아래가 Ⅳ단원 공용 골격입니다. 6·7·8·9·10차시가 이 함수들을 글자 그대로 다시 싣습니다. 지금은 다섯 곳이 ?????로 비어 있어 실행하면 문법 오류로 멈춥니다. 채워야 돌아갑니다. 힌트는 각 빈칸 바로 위 주석에 있습니다.

빈칸어디묻는 것틀리면
①column()빈칸이 아닌 값만 남기는 조건!= 0으로 쓰면 0이 통째로 빠진다
②clean()빈칸을 무엇으로 메울까평균이면 투명도가 56.25가 된다
③minmax_apply()0~1로 옮기는 식나누기를 빼면 자 맞추기가 무의미해진다
④split()자르기 전에 해야 할 한 줄안 섞으면 테스트가 한 집단으로 쏠린다
⑤【5】 자 맞추기minmax_fit()에 무엇을 넣나오류 없이 조용히 누수한다 — 벗어난 칸 3 → 0
⑤만 틀려도 프로그램이 멈추지 않습니다. 화면의 "← 같다"가 다섯 줄 다 뜨면 틀린 것입니다.
⚠️ 다 채웠는데 안 돌아갈 때
  • SyntaxError — ?????가 아직 남아 있습니다. 다섯 곳을 다 찾았는지 확인하세요.
  • IndentationError — 빈칸을 채우면서 앞의 공백을 지웠습니다. 원래 줄과 같은 칸에서 시작해야 합니다.
  • ValueError: 열 수가 안 맞는 줄 — DATA에 붙여 넣다 쉼표를 빠뜨렸습니다. 오류가 알려 준 줄 번호를 보세요.
  • ZeroDivisionError: division by zero — FEATURES에 값이 모두 같은 열이 들어갔습니다. 그 열을 빼세요. (이 죽음은 Ⅳ-8에서 다시 만납니다.)
나란히 앉은 두 사람 가운데 한 사람이 검은 바탕의 코드 화면을 손가락으로 짚고, 다른 사람은 키보드에 손을 올리고 있는 모습
빈칸 다섯 곳은 둘이 한 화면을 보며 채우는 것이 빠릅니다. 한 사람이 읽고 한 사람이 칩니다. 특히 ⑤번은 오류가 안 나므로, 눈이 둘이어야 잡힙니다. 출처: Vajrapani666, Wikimedia Commons (CC BY-SA 3.0)

활동 ③ 우리 팀 데이터를 넣는다 (5분)

코드가 한 번 돌았으면, 이제 【0】 상자만 우리 것으로 갈아 끼웁니다. 그 아래 함수는 한 글자도 고치지 않습니다. 고치고 싶어지면 손을 멈추고 다시 읽어 보세요 — 고쳐야 할 것 같은 이유는 대개 【0】 상자를 덜 고쳤기 때문입니다.

항목최소 규격왜 그런가
행 수70행 이상
(집단까지 채우려면 100행)
30%를 테스트로 떼면 테스트가 21개가 되어 20개 규격을 넘는다. 다만 아래 '집단 크기'까지 맞추려면 테스트가 30개는 되어야 하므로 100행쯤이 필요하다
특성(수치)3~6개2개면 볼 것이 없고, 너무 많으면 시간 안에 못 끝낸다
이름표두 가지세 가지 이상은 Ⅳ-9의 네 칸이 받지 못한다
이름표 균형적은 쪽 30% 이상90:10이면 기준선이 90%라 넘기 어렵다
집단 열1개, 값 2~3가지없으면 Ⅳ-9가 통째로 안 돈다
집단 크기테스트에서 15개 이상그 아래면 %를 성능이라 부를 수 없다
상수 열넣지 않는다minmax_apply가 0으로 나누다 죽는다
규격에 못 미쳐도 오늘은 그대로 넣어 보세요. 경고가 무엇을 짚는지 보는 것이 목적입니다.

갈아 끼울 때 다섯 줄을 이렇게 맞춥니다.

  • NAMES — 열 이름을 왼쪽부터 차례대로. DATA 한 줄의 값 개수와 같아야 합니다.
  • FEATURES — 모델에 넣을 열만. 수치 열만 넣습니다. 이름표와 집단은 넣지 않습니다.
  • LABEL — 맞히려는 열 이름 하나.
  • GROUP — 나중에 성능을 쪼개어 볼 열 하나. 촬영 조건·요일·학년·수집한 사람 무엇이든 좋습니다.
  • POSITIVE — 이름표 두 가지 중 찾아내려는 쪽. 이것을 안 바꾸면 Ⅳ-9의 네 칸이 통째로 뒤집힙니다.
ℹ️ 우리 표에 빈칸이나 튀는 값이 하나도 없다면

그것도 결과입니다. 화면에 빈칸 합계 0칸 (없다)과 이상치(z>3) 없음이 찍히지요. 다만 한 가지는 확인하세요 — 빈칸을 이미 0으로 채워 넣지는 않았나요? 0은 "0이라고 적혀 있다"이고 빈칸은 "아무것도 안 적혀 있다"입니다. 0으로 채운 표는 코드가 빈칸을 못 찾습니다. 모르는 칸은 비워 두고 None으로 적으세요.

📖

정리 — 오늘 손에 남은 것

오늘은 모델을 하나도 안 만들었습니다. 그런데도 이 차시가 프로젝트의 실제 출발점인 까닭은, 7·8·9·10차시가 전부 오늘 만든 두 뭉치 위에서 돌기 때문입니다. 아래 여섯 줄이 채워졌으면 오늘은 끝난 것입니다.

  • 표가 코드에 들어갔다. load()가 열 수를 통과시켰다(ValueError가 안 났다).
  • 빈칸이 몇 칸인지 안다. 몇 번째 행의 어느 열인지, 무엇으로 메웠는지 적었다.
  • 튀는 값을 사람이 판정했다. 표시된 값을 눈으로 보고 버릴지 둘지 정했고, 그 까닭을 한 줄로 적었다.
  • 훈련과 테스트가 갈렸다. 몇 개 대 몇 개인지, 씨앗이 얼마인지 적었다.
  • 크기 경고가 없다. 경고가 떴다면 어떤 경고인지 적었다(그것도 오늘의 산출물이다).
  • 자를 훈련으로만 쟀다. "← 같다"가 다섯 줄 다 뜨지는 않았다.

예시 산출물 — 실험 기록의 첫 줄

Ⅳ-10에서 실험 기록표를 만들 텐데, 그 첫 줄이 오늘 것입니다. 아직 성능 칸은 비어 있습니다. 아래는 견본 데이터로 채운 예시입니다.

#무엇을 했나행훈련/테스트씨앗사람이 정한 것
0원본 표를 그대로 읽음160——빈칸 3칸 · 튀는 값 2개 확인
1중앙값으로 메우고, 오타 두 줄을 버림158110 / 4842 밝기 1210은 121의 오타, 투명도 722.0은 72.2의 오타로 판단
"사람이 정한 것" 칸을 비워 두지 마세요. 그 칸이 비면 Ⅳ-11에서 무엇을 설명할지가 사라집니다.
⚠️ 여기까지 못 했으면 이렇게 하세요

우리 데이터를 못 넣었다 → 견본 160행을 그대로 두고 활동 ①·②만 끝내세요. 견본으로도 오늘 배울 것은 다 배웁니다. 팀 데이터는 다음 시간 첫 5분에 갈아 끼우면 되고, 【0】 상자만 바꾸면 되므로 5분이면 충분합니다.

행이 70개가 안 된다 → 그대로 넣어 경고를 확인하고, 경고 문구를 그대로 옮겨 적으세요. 그리고 몇 행을 더 모아야 하는지 숫자로 적어 두세요. 다음 시간까지 채우면 됩니다.

빈칸 다섯 곳을 못 채웠다 → ⑤번 하나만이라도 채워 보세요(feats_tr). 나머지 넷은 답이 힌트 주석에 거의 다 적혀 있습니다. 그래도 안 되면 짝 팀의 화면을 보고 채운 뒤, 왜 그 답인지를 자기 말로 한 줄 적으면 됩니다.

🎯 다음 시간까지 반드시 정해 올 것 — 집단 열 하나

우리 표에서 성능을 쪼개어 볼 열을 하나 정해 오세요. 촬영 조건 · 요일 · 학년 · 수집한 사람 · 계절 — 무엇이든 좋습니다. 다만 테스트에서 한 집단이 15개는 넘어야 합니다.

이 열이 없으면 Ⅳ-9가 통째로 안 돕니다. 전체 정확도 한 줄만 남고, "그 85%가 누구의 85%인가"를 물을 수가 없게 됩니다. 지금은 모델에 넣지 않는 열이라 쓸모없어 보이지만, 이 단원에서 가장 중요한 열이 됩니다.

다음 시간(Ⅳ-7)에는 오늘 만든 두 뭉치 위에 k-NN 모델을 얹습니다. 한 가지만 미리 알아 두세요 — 파이썬 실행기는 차시마다 새로 뜨기 때문에 오늘 만든 train·test 변수가 다음 페이지로 넘어가지 않습니다. Ⅳ-7은 오늘 코드를 그대로 다시 싣고 그 아래에 모델을 붙입니다. 그래서 오늘 골격을 한 번 돌려 본 팀은 다음 시간이 훨씬 빠릅니다.

🔁 되돌아보기

오늘 우리 표에서 빈칸과 튀는 값을 사람이 판정하고, 섞어 나눈 뒤 훈련으로만 자를 재어 훈련·테스트 두 뭉치를 만들었다. Ⅱ-5가 남긴 물음 — 0~1을 벗어나는 값 — 의 답도 우리 데이터에서 확인했다. 다음 시간에는 이 두 뭉치 위에 첫 모델을 얹고, 기준선을 넘는지 잰다.

✅

확인 문제

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

1. 정규화가 왜 필요한지를, 단위가 크게 다른 두 열을 예로 들어 설명하세요. 견본 데이터의 반사도와 밝기를 쓰되, 두 열의 범위와 폭의 비를 함께 적으세요.
📖 모범 답안

반사도는 0.43~1.7, 밝기는 80~335입니다. 벌어진 폭이 반사도 1.27, 밝기 255이므로 약 201배 차이가 납니다. (훈련 110개만으로 재면 밝기 폭이 251이라 약 198배가 됩니다. 자를 어디서 재느냐에 따라 이 숫자도 달라진다는 것이 오늘의 이야기이기도 합니다.)

k-NN은 두 점 사이의 거리를 각 특성의 차를 제곱해 더해서 잽니다. 폭이 200배쯤이면 제곱은 약 4만 배가 되지요. 실제로 훈련 110개에서 재 보면 거리 제곱의 91.66%를 '밝기' 하나가 가져갑니다. (전체 158개로 재면 91.45%입니다. 어느 쪽이든 나머지 네 특성을 다 합쳐도 10%가 안 됩니다.) 반사도가 아무리 달라도 거리는 거의 안 움직입니다.

정규화는 이 불균형을 없앱니다. 다만 "정규화하면 정확도가 오른다"고 쓰면 안 됩니다. 정규화의 목적은 정확도를 올리는 것이 아니라, 단위를 무엇으로 재느냐에 따라 답이 달라지는 일을 막는 것입니다. 밝기를 그대로 두든 100으로 나누든 결과가 같아야 하는데, 정규화가 없으면 그렇지 않습니다.

2. 훈련과 테스트로 나누기 전에 섞지 않으면 어떤 일이 생기나요? 견본에서 실제로 나온 결과를 들어 설명하고, 실제 팀이 데이터를 모을 때 이 사고가 왜 잘 나는지도 쓰세요.
📖 모범 답안

훈련과 테스트가 서로 다른 세상을 보게 됩니다. 견본을 촬영조건 순으로 정렬해 두고 안 섞은 채 앞에서 70%를 자르면, 테스트 48개가 전부 '어두움'이 됩니다. 모델은 밝은 사진만 보고 배운 뒤 어두운 사진으로만 시험을 치릅니다. 그 성적표는 우리 모델의 실력이 아니라 분할이 만든 숫자입니다.

실제 팀은 대개 조건별로 몰아서 모읍니다. 밝은 날 하루에 다 찍고, 흐린 날 또 몰아 찍는 식이지요. 날짜 순으로 모으면 3월 데이터가 앞, 6월 데이터가 뒤에 쌓입니다. 모은 순서에는 거의 언제나 이런 규칙이 숨어 있어서, 그대로 자르면 규칙까지 함께 잘립니다.

주의할 점 하나. 견본을 그대로(정렬하지 않고) 안 섞고 자르면 밝음 30 · 어두움 18이 나와 별일 없어 보입니다. 이 견본이 이미 섞인 상태로 적혀 있기 때문이지, 안 섞어도 된다는 뜻이 아닙니다. 섞는 것은 운에 기대지 않기 위한 규칙입니다.

3. (오늘 돌린 결과) 관제판을 기본값 그대로 두고 ① 남은 행 ② 훈련 ③ 테스트 ④ 테스트의 밝음/어두움 ⑤ 0~1을 벗어난 칸을 적으세요. 그다음 '정규화 자'만 '전체로'로 바꾸면 다섯 숫자 중 무엇이 달라지고 무엇이 그대로인지 적고, 그 변화를 어떻게 읽어야 하는지 한 문장으로 쓰세요.
📖 모범 답안

기본값: 남은 행 158 · 훈련 110 · 테스트 48 · 밝음 28 / 어두움 20 · 벗어난 칸 3칸.

자를 '전체로' 바꾸면 벗어난 칸만 3칸 → 0칸으로 바뀌고, 나머지 넷은 그대로입니다. 자를 재는 방법을 바꾼 것이지 행을 나누는 방법을 바꾼 것이 아니니까요. 달라지는 것은 자 두 줄뿐입니다 — 밝기가 [80, 331]에서 [80, 335]로, 이물질이 [0.9, 39.2]에서 [0.8, 39.6]으로.

읽는 법: 0칸이 나왔으면 잘한 것이 아니라 누수를 의심해야 한다. 벗어난 칸이 하나도 없다는 것은 테스트의 끝값이 이미 자 안에 들어가 있다는 신호일 수 있습니다. (테스트가 아주 작아 끝값을 못 건드려도 0칸이 나올 수 있으니, 그때는 씨앗을 바꿔 다시 재 보세요.)

4. (오늘 돌린 결과) 파이썬 【5】 화면의 자 두 벌 견주기에서, "← 같다"가 붙은 특성과 안 붙은 특성을 각각 적으세요. 그리고 왜 어떤 줄은 같고 어떤 줄은 갈렸는지 설명하세요.
📖 모범 답안

같다: 반사도 [0.43, 1.7] · 투명도 [8.4, 96.3] · 찌그러짐 [0, 10] — 세 줄.
갈렸다: 밝기 [80, 335] 대 [80, 331] · 이물질 [0.8, 39.6] 대 [0.9, 39.2] — 두 줄.

갈린 까닭은 그 열의 끝값이 테스트 쪽에 떨어졌기 때문입니다. 밝기의 최댓값 335를 가진 사진과, 이물질의 최솟값 0.8·최댓값 39.6을 가진 사진이 섞은 결과 테스트 뭉치로 갔습니다. 훈련만 보면 그 값들이 안 보이니 자가 조금 짧아집니다. 반대로 반사도·투명도·찌그러짐은 양 끝값이 마침 훈련 쪽에 있어서 두 자가 같아졌습니다.

그러니 "두 줄이 갈린다"는 이 데이터·이 씨앗의 결과일 뿐이고, 우연입니다. 씨앗을 바꾸면 갈리는 열도 바뀝니다(씨앗 3에서는 밝기가 [92, 335]가 됩니다). 중요한 것은 몇 줄이 갈렸느냐가 아니라, 갈릴 수 있다는 사실 자체입니다. 갈릴 수 있다는 것은 전체로 자를 재면 테스트를 들여다본 셈이 된다는 뜻이니까요.

5. 견본 데이터에서는 자를 전체로 재도(누수를 해도) 다음 시간에 만들 모델의 정확도가 거의 오르지 않습니다. 그런데도 누수를 하면 안 되는 까닭은 무엇인가요? "성능이 부풀려지니까"보다 정확한 답을 쓰세요.
📖 모범 답안

문제는 좋아 보이는 것이 아니라, 그 숫자가 정직한지를 우리가 확인할 수 없게 되는 것입니다.

min-max 정규화의 누수는 새어 나가는 정보가 열마다 숫자 둘(최솟값·최댓값)뿐인, 가장 약한 누수입니다. 그래서 이 견본에서는 정확도가 거의 안 움직입니다. 하지만 그것은 이번에 마침 약했다는 뜻이지, 누수가 안전하다는 뜻이 아닙니다.

누수에는 훨씬 센 것들이 있습니다. 이름표가 새는 경우(맞혀야 할 답이 특성 열에 섞여 들어간 것), 같은 사진이 훈련과 테스트에 둘 다 들어간 경우가 그렇습니다. 그때는 정확도가 통째로 거짓말이 됩니다. 문제는, 결과만 보고는 이번 누수가 약한 것이었는지 센 것이었는지 구별할 방법이 없다는 점입니다.

그래서 순서를 규칙으로 굳혀 둡니다 — 나누고, 훈련으로만 자를 재고, 그 자로 테스트를 잰다. 규칙을 지켰다는 것 자체가, 성적표를 믿어도 된다는 유일한 근거입니다.

6. 우리 팀 데이터(없으면 견본)에서 빈칸과 튀는 값을 어떻게 처리했는지, 그리고 왜 그렇게 정했는지를 각각 한 문장으로 쓰세요. 또 다음 시간까지 정해 올 집단 열을 무엇으로 골랐고, 그 집단이 테스트에서 몇 개가 될지 어림해 보세요.
📖 모범 답안

견본으로 쓴 예시. 빈칸 3칸은 그 열의 중앙값으로 메웠다(반사도 1.1 · 투명도 50.2 · 이물질 21.2). 평균이 아니라 중앙값을 쓴 까닭은, 아직 안 버린 722.0이 평균을 끌어올려 투명도를 56.25로 메우게 만들기 때문이다. 중앙값은 그 한 칸을 무시한다.

튀는 값 2칸(밝기 1210 · 투명도 722.0)은 행째로 버렸다. 이 사진기의 밝기 범위는 80~335이고 투명도는 %라서 0~100인데, 두 값 모두 자릿수가 하나씩 어긋나 있었다. 121에 0을 하나 더 쳤고, 72.2의 소수점이 빠진 것이라고 판단했다. 코드가 z 규칙으로 표시해 주었지만, 버릴지 말지는 사람이 정했다.

집단 열은 '촬영조건'(밝음/어두움)으로 골랐다. 사진을 어떤 빛에서 찍었는지가 측정값 전체에 영향을 주기 때문이다. 정제 뒤 밝음 87 · 어두움 71이므로 30%를 테스트로 떼면 각각 26개·21개쯤 될 것으로 어림했다. 실제로는 밝음 28 · 어두움 20이 나왔고, 둘 다 15를 넘으므로 %를 성능이라 불러도 된다.

답을 쓸 때 "왜 그렇게 정했는가"를 빼지 마세요. 처리 방법은 여러 개가 정답일 수 있지만, 까닭이 없는 처리는 Ⅳ-11에서 설명할 수가 없습니다.

🔎

더 알아보기

오늘 한 세 가지 — 메우기 · 섞기 · 씨앗 — 를 한 겹 더 들여다보기

MCAR MAR MNAR 완전히 우연 보이는 열 탓 빈 값 자체 탓 옮겨 적다가 한 칸 빠뜨림 어두운 사진에서만 투명도를 못 잼 이물질이 너무 많아 잴 수가 없었음 중앙값으로 메워도 무난 촬영조건별로 따로 메운다 메우면 값이 한쪽으로 쏠린다 빈칸이 '왜' 비었는지가 메우는 법을 정한다 코드는 빈칸을 찾아 줄 뿐, 까닭은 사람이 적는다
오해 바로잡기

빈칸은 다 같은 빈칸이 아니다 — 비어 있는 까닭 세 갈래

오늘 골격은 빈칸을 모두 그 열의 중앙값으로 메웠습니다. 견본의 세 칸은 "측정하다 놓쳤거나 옮겨 적다 빠뜨린 자리"라는 설정이라 이것으로 충분했어요. 그런데 빈칸이 왜 생겼느냐에 따라 같은 처리가 옳기도 하고 틀리기도 합니다. 통계학자 도널드 루빈(Donald Rubin)이 1976년에 이 까닭을 세 갈래로 나누었고, 지금도 그 이름을 그대로 씁니다.

MCAR(완전히 무작위로 빠짐)은 어느 값과도 상관없이 우연히 빈 경우입니다. 이때는 중앙값으로 메워도 크게 틀어지지 않습니다. MAR(무작위로 빠짐)은 표에 보이는 다른 열 때문에 빈 경우예요. 어두운 사진에서만 투명도를 못 쟀다면, 밝은 사진까지 섞은 전체 중앙값보다 같은 촬영조건끼리의 중앙값이 더 그럴듯한 값입니다.

가장 까다로운 것은 MNAR(무작위가 아니게 빠짐)입니다. 이물질이 너무 많이 묻어 아예 잴 수 없었던 병이라면, 빈칸의 진짜 값은 큰 쪽에 몰려 있을 거예요. 그 자리를 중앙값으로 메우면 가장 더러운 병들이 '보통 병'으로 둔갑합니다. 이런 빈칸은 표만 봐서는 코드가 알아챌 방법이 없어요. 그래서 실험 기록에 "무엇으로 메웠는지"를 적을 때 왜 비었는지도 한 줄 함께 적어 두어야 나중에 설명할 수 있습니다.

지붕 달린 창고 안에 압축한 페트병 더미가 왼쪽은 파란 병, 가운데는 투명한 병, 오른쪽은 초록 병끼리 나뉘어 쌓여 있는 모습
현장

무엇 단위로 섞을까 — 같은 병, 같은 시기

사진은 체코 올로모우츠의 한 재활용장입니다. 압축한 페트병 더미가 파랑 · 투명 · 초록 색깔별로 따로 쌓여 있지요. 현실의 데이터도 이렇게 무리 지어 들어옵니다. 오늘 본 것처럼 촬영조건 순으로 정렬된 표를 그대로 자르면 테스트가 한 무리로 쏠리고, 그래서 먼저 섞었습니다. 그런데 섞기에는 한 가지 더 따질 것이 있습니다 — 무엇을 한 덩어리로 보고 섞느냐입니다.

팀이 같은 병을 각도만 바꿔 세 장 찍었다고 해 봅시다. 행 하나하나를 섞으면 그 병의 두 장은 훈련으로, 한 장은 테스트로 갈 수 있어요. 그러면 모델은 시험 문제의 병을 이미 본 채로 시험을 칩니다. 확인 문제 5에서 말한 "같은 사진이 훈련과 테스트에 둘 다 들어간" 누수의 가까운 친척이지요. 그래서 병 번호 같은 묶음 열을 두고, 한 병의 사진은 통째로 한쪽에만 보냅니다. 이것을 그룹 분할이라 하고, 의료 영상에서 사진이 아니라 환자 단위로 나누는 것도 같은 까닭입니다.

반대로 섞으면 안 되는 때도 있습니다. 내일의 미세먼지를 예측하는 모델이라면, 섞는 순간 미래의 날들이 훈련에 들어가 과거를 맞히는 꼴이 됩니다. 이때는 앞 시기를 훈련, 뒤 시기를 테스트로 순서대로 자릅니다. 파이썬 라이브러리 scikit-learn에도 이 두 경우를 위한 GroupKFold와 TimeSeriesSplit이 따로 있어요. 기준은 하나입니다 — 테스트가 모델이 실제로 쓰일 상황을 닮았는가.

사진: 색깔별로 나누어 쌓은 압축 페트병(체코 올로모우츠) · 출처: Michal Maňas, Wikimedia Commons (CC BY 3.0)

같은 씨앗 → 같은 섞임 씨앗 42 씨앗 42 씨앗 3 MT19937 상태 624칸 규칙대로 계산 몇 번을 돌려도 똑같다 씨앗이 다르면 다른 표 훈련으로 간 행 테스트로 간 행 '무작위'처럼 보이지만 실은 정해진 계산이다
원리 더 깊이

씨앗 42의 정체 — 무작위인데 왜 매번 같은가

컴퓨터의 난수는 대부분 의사 난수(pseudo-random)입니다. 주사위를 굴리는 것이 아니라, 정해진 계산 규칙에 출발값 하나를 넣고 차례로 숫자를 뽑아내는 것이지요. 그 출발값이 바로 씨앗입니다. 규칙이 정해져 있으니 씨앗이 같으면 뽑히는 숫자도, 그 숫자로 섞은 순서도 늘 같습니다. 오늘 split()이 몇 번을 돌려도 훈련 110 · 테스트 48 · 밝음 28 · 어두움 20을 내놓는 까닭입니다.

파이썬의 random 모듈이 쓰는 규칙은 1998년 마쓰모토 마코토와 니시무라 다쿠지가 발표한 메르센 트위스터(MT19937)입니다. 안에 32비트 숫자 624칸짜리 상태를 들고 있고, 같은 순서가 되풀이되기까지의 주기가 219937−1로 어마어마하게 깁니다. 오늘의 관제판이 파이썬과 글자까지 같은 숫자를 내는 것도, 이 규칙을 자바스크립트로 그대로 옮겨 두었기 때문이에요.

두 가지를 기억해 두세요. 첫째, 42는 특별한 수가 아닙니다 — 어떤 씨앗이든 적어 두기만 하면 됩니다. 과제 ②의 씨앗 여덟 개를 차례로 넣어 보면 벗어난 칸이 적게는 1칸(씨앗 5), 많게는 8칸(씨앗 7)까지 흔들리니, 좋은 결과가 나오는 씨앗을 골라 쓰는 것은 그 자체로 테스트를 들여다보는 일입니다. 둘째, 메르센 트위스터는 앞의 출력을 충분히 보면 다음 값을 알아낼 수 있어서 비밀번호나 인증 코드에는 쓰면 안 됩니다. 그런 용도로는 파이썬의 secrets 모듈을 씁니다.