단원 홈
2단원 · 5차시

센티미터와 걸음 수를 나란히
단위를 맞추는 일

4차시에서 빈칸을 메우고 이상치를 가려내 데이터를 깨끗하게 만들었습니다. 그 데이터로 나와 가장 비슷한 친구를 찾아봅시다. 그런데 걸음 수의 단위를 바꿔 적었더니 답이 통째로 뒤집혔습니다. 어느 답을 믿어야 할까요?

성취기준 12인기02-02
유클리드 거리정규화 min-max 표준화 z 점수훈련 기준 데이터 누수이상치
🎯 학습 목표
  • 두 사람의 유클리드 거리를 식에 값을 넣어 직접 계산하고, 단위가 큰 특성이 어떻게 판정을 독점하는지 숫자로 설명할 수 있다.
  • min-max 정규화와 z 점수 표준화의 식을 각각 쓰고, 이상치가 섞였을 때 어느 쪽이 덜 망가지는지 실험으로 확인할 수 있다.
  • 변환 기준을 훈련 데이터에서만 뽑아야 하는 까닭을 데이터 누수와 이어 설명하고, 정규화가 필요 없는 알고리즘의 예를 들 수 있다.
🤔

여는 장면 — 나와 가장 비슷한 친구는 누구인가

4차시에서 우리는 학생 52명의 기록을 손질했습니다. 빈칸 여덟 자리를 어떻게 메울지 정하고, 키 1750.0이라는 자릿수 오타를 찾아내고, 진짜로 큰 학생과 잘못 적힌 값을 가려내는 데 한 시간을 썼지요. 이제 데이터는 깨끗합니다. 그런 데이터로 무엇을 할 수 있을까요?

기계학습이 하는 일 중 가장 단순한 것부터 해 봅시다. 비슷한 것 찾기입니다. "나와 생활 습관이 가장 닮은 친구는 누구인가?" 이 물음에 답할 수 있으면 "이 학생에게는 어떤 학습 방법이 맞을까"도, "이 손님에게 무엇을 추천할까"도 같은 방식으로 답할 수 있습니다. 8차시에서 배울 k-최근접 이웃이 정확히 이 물음 위에 서 있습니다.

손으로 끝까지 따라갈 수 있게 아주 작은 표를 하나 만들었습니다. 우리 반 친구 셋과 나, 그리고 특성은 딱 두 개입니다 — 하루 걸음 수와 하루 공부 시간.

사람하루 걸음 수 (걸음)하루 공부 시간 (시간)
나50002.0
가람51004.5
나래20002.1
다온40003.0
친구 이름을 '가·나·다'로 쓰면 셋째 친구 '나'와 나 자신이 화면에서 구별되지 않습니다. 그래서 첫 글자만 지켜 가람·나래·다온으로 부르기로 합니다.

표를 눈으로 훑어봅시다. 누가 나와 가장 비슷해 보입니까? 걸음 수만 보면 가람입니다. 5100과 5000, 겨우 100걸음 차이니까요. 공부 시간만 보면 나래입니다. 2.1과 2.0, 0.1시간 차이입니다. 둘을 함께 보면 어떻게 될까요? 그것을 정하는 것이 오늘의 일입니다.

💭 오늘의 물음

단위를 바꿨을 뿐인데 '가장 비슷한 친구'가 달라진다면,
그 답은 대체 무엇을 말해 주고 있는 걸까?

미리 결과를 말해 두겠습니다. 위 네 사람에게 오늘 배울 계산을 그대로 적용하면 1등이던 가람이 3등으로 내려가고, 2등이던 다온이 1등으로, 3등이던 나래가 2등으로 올라옵니다. 두 사람이 자리를 맞바꾸는 정도가 아니라 셋이 전부 자리를 옮기는 것이지요. 데이터는 한 글자도 고치지 않았는데 말입니다. 오늘 15분 동안 여러분은 그 뒤집힘을 직접 일으켜 보고, 왜 일어나는지 손으로 계산해 확인할 것입니다.

호숫가 바위 위에 놓인 나무 접자. 아래 줄에는 1부터 12까지 센티미터 눈금이, 그 위 줄에는 BURGOS라는 글자와 함께 1부터 5까지 인치 눈금이 새겨져 있다
나무 접자 한 개에 두 가지 눈금이 새겨져 있습니다. 아래 줄은 센티미터, 위 줄은 옛 스페인 단위인 부르고스 인치입니다. 같은 길이인데 아래 눈금으로 10인 자리가 위 눈금으로는 4를 조금 넘습니다. 출처: R. Henrik Nilsson, Wikimedia Commons (CC BY 4.0)

사진의 자는 같은 길이에 두 가지 숫자를 붙여 놓았습니다. 10이라는 숫자가 아래 눈금에서는 10센티미터지만 위 눈금에서는 한참 더 긴 길이를 가리킵니다. 사람은 이것을 헷갈리지 않습니다. 숫자 뒤에 붙은 단위를 함께 읽으니까요. 오늘 우리는 여기서 한 걸음 더 나가, 걸음 수와 공부 시간처럼 재는 대상부터 다른 두 숫자를 한 식에 나란히 집어넣습니다.

그런데 컴퓨터에게 우리는 보통 숫자만 넘깁니다. [5000, 2.0]이라고 적어 주면 컴퓨터는 5000과 2.0을 그냥 두 개의 수로 봅니다. 앞의 것이 걸음이고 뒤의 것이 시간이라는 사실은 어디에도 적혀 있지 않습니다. 그래서 걸음 수 쪽 숫자가 크다는 그 이유 하나만으로 그쪽이 판정을 독점하게 됩니다. 오늘 배울 정규화는 그 착각을 바로잡는 일입니다.

1

'비슷하다'를 숫자로 — 거리라는 자

'비슷하다'는 말은 그대로는 계산할 수 없습니다. 컴퓨터에게 시키려면 숫자로 바꿔야 합니다. 가장 널리 쓰는 방법은 두 사람을 점 두 개로 보고 그 사이의 거리를 재는 것입니다. 거리가 짧으면 비슷하고, 멀면 다릅니다. 아주 단순하지만 그래서 어디에나 쓰입니다.

중학교에서 배운 좌표평면 위 두 점 사이의 거리 공식을 그대로 씁니다. 가로축을 걸음 수, 세로축을 공부 시간으로 놓으면 나는 (5000, 2.0), 가람은 (5100, 4.5)인 점입니다. 이 거리를 유클리드 거리(Euclidean distance)라고 부릅니다.

거리 = √( (걸음 차)2 + (공부 차)2 )

특성이 두 개면 항이 두 개, 네 개면 항이 네 개입니다. 항을 더 붙이기만 하면 특성이 몇 개든 똑같이 잽니다. 15차시에서는 특성 네 개짜리 데이터에 이 식을 그대로 쓰게 됩니다. 그래서 오늘 짤 함수는 특성 수를 미리 정하지 않고 len(a)만큼 도는 꼴로 만들 것입니다.

값을 넣어 봅시다

가람과의 거리부터 손으로 계산합니다.

가람 : √( (5000−5100)2 + (2.0−4.5)2 ) = √( 10000 + 6.25 ) = √10006.25 = 100.0312

나머지 둘도 같은 방식입니다.

나래 : √( 30002 + 0.12 ) = √( 9000000 + 0.01 ) = 3000.0
다온 : √( 10002 + 1.02 ) = √( 1000000 + 1.0 ) = 1000.0005

가장 가까운 사람은 가람입니다. 순위는 가람 < 다온 < 나래. 여러분이 표를 눈으로 보고 짐작한 것과 같습니까? 아마 그럴 것입니다. 그런데 이 답에는 심각한 문제가 하나 숨어 있습니다.

거리 안을 뜯어 보면

계산 과정에서 나온 두 제곱 값을 나란히 놓아 봅시다. 왼쪽이 걸음 수 쪽에서 나온 제곱, 오른쪽이 공부 시간 쪽에서 나온 제곱입니다.

거리 안에 들어간 두 제곱 (가로 눈금은 10배마다 한 칸) 0.01 1 100 1만 100만 가람 10,000 6.25 나래 9,000,000 0.01 ← 막대가 거의 보이지 않는다 다온 1,000,000 1.0 걸음 차의 제곱 공부 차의 제곱

그림 1. 가로 눈금은 한 칸이 10배입니다(로그 눈금). 그래야 0.01과 900만을 한 그림에 담을 수 있습니다. 나래의 주황 막대는 눈금을 잡아 늘려도 보이지 않을 만큼 짧습니다.

나래를 봅시다. 걸음 쪽에서 9,000,000이 나왔고 공부 쪽에서 0.01이 나왔습니다. 비율로 9억 배입니다. 이 둘을 더하면 어떻게 될까요? 9,000,000 + 0.01 = 9,000,000.01. 소수점 아래 둘째 자리에만 흔적이 남습니다. 제곱근을 씌우면 그마저 사라져 3000.0이 됩니다.

즉 공부 시간은 계산에 참여하지 못했습니다. 우리는 특성 두 개로 비슷함을 재는 프로그램을 짰다고 생각했지만, 실제로 답을 정한 것은 걸음 수 하나뿐이었습니다. 세 사람의 격차를 각각 계산해 보면 나래가 9억 배(9,000,000 : 0.01), 다온이 100만 배(1,000,000 : 1.0), 격차가 가장 작은 가람도 1,600배(10,000 : 6.25)입니다. 어느 쪽도 대등한 싸움이 아닙니다.

⚠️ 왜 하필 큰 숫자가 이기는가

거리 식은 각 특성의 차를 제곱해서 더합니다. 제곱은 큰 차이를 더 크게 벌립니다. 걸음 수의 차는 보통 수백~수천이고 공부 시간의 차는 기껏해야 몇입니다. 3000과 0.1의 차이가 제곱을 거치면 9,000,000과 0.01이 되지요. 합에 들어가는 순간 작은 쪽은 반올림으로 사라집니다. 문제는 걸음 수가 더 중요해서 이긴 것이 아니라는 점입니다. 단지 숫자를 적을 때 쓴 단위가 컸을 뿐입니다.

2

자를 바꾸면 답이 바뀐다 — 단위라는 우연

앞의 주장이 정말인지 확인하는 방법이 있습니다. 데이터는 그대로 두고 단위만 바꿔 다시 재 보는 것입니다. 값이 같은 것을 다르게 적었을 뿐이니, 제대로 된 방법이라면 답이 같아야 합니다.

걸음 수를 거리로 바꿔 적어 봅시다. 한 걸음을 0.7m로 잡으면 한 걸음은 0.0007km입니다. 가람의 5100걸음은 3.57km, 나의 5000걸음은 3.5km가 됩니다. 같은 사람, 같은 하루, 같은 사실입니다. 적는 방법만 달라졌습니다.

파란 천 위에 놓인 기계식 만보기. 허리띠에 거는 금속 고리가 달려 있고, 검은 눈금판에 Kilometerzähler라는 글자와 2부터 20까지의 빨간 숫자, 흰 바늘이 있다
1970년 무렵 독일에서 만든 기계식 만보기입니다. 허리에 차면 걸음마다 속의 추가 흔들려 수를 세는데, 눈금판에는 걸음이 아니라 킬로미터(Kilometerzähler, '킬로미터 계수기')가 적혀 있습니다. 센 걸음에 맞춰 둔 보폭을 곱해 거리로 보여 주는 것이지요 — 아래 표에서 우리가 하는 일과 같습니다. 출처: Sarang, Wikimedia Commons (Public domain)
사람걸음 수로 적으면km로 적으면공부 시간
나50003.52.0
가람51003.574.5
나래20001.42.1
다온40002.83.0

이제 km로 적힌 표에 같은 거리 식을 씁니다. 가람과의 거리는 √( (3.5−3.57)2 + (2.0−4.5)2 ) = √( 0.0049 + 6.25 ) = 2.501입니다. 아까는 걸음 쪽이 10000이고 공부 쪽이 6.25였는데, 이번에는 걸음 쪽이 0.0049입니다. 이번에는 공부 시간이 이겼습니다.

자가람나래다온가장 비슷한 친구
걸음 수 그대로100.03123000.01000.0005가람
km로 바꿔 적음2.5012.10241.2207다온
같은 사람, 같은 하루. 단위를 바꿔 적었을 뿐인데 1등이 바뀝니다.

답이 바뀌었습니다. 걸음 수로 적으면 가람이 1등, km로 적으면 다온이 1등입니다. 데이터를 고친 적이 없는데도 그렇습니다. 이것이 무엇을 뜻하는지 분명히 해 둡시다.

💭 여기서 멈춰 생각하기

두 답 중 하나는 틀린 답일까요? 아닙니다. 둘 다 틀렸습니다. '누가 나와 비슷한가'라는 물음의 답이 '걸음 수를 무엇으로 적었는가'에 달려 있다면, 그 답은 데이터에 대해 아무것도 말해 주지 않습니다. 우리가 알아낸 것은 친구들에 관한 사실이 아니라 우리가 표를 적은 방식일 뿐입니다.

반대쪽으로도 해 볼 수 있습니다. 걸음 수를 그대로 두고 공부 시간에 배율을 곱해 크기를 키우는 것입니다. 공부 시간을 분으로 바꾸면 60배가 됩니다. 이러면 답이 바뀔까요? 실제로 돌려 보면 ×60으로는 순위가 하나도 안 바뀝니다. 여전히 가람 < 다온 < 나래입니다. 60배로는 걸음 수를 이길 수 없다는 뜻이지요.

공부 시간에 곱한 배율순위1등
×1 (시간)가람 < 다온 < 나래가람
×60 (분)가람 < 다온 < 나래가람
×434가람 < 다온 < 나래가람
×435다온 < 가람 < 나래다온
×3600 (초)나래 < 다온 < 가람나래
1등이 처음 바뀌는 자리는 약 434.25배입니다. 초 단위(×3600)까지 가면 순위가 완전히 뒤집혀 정규화한 답과도 정반대가 됩니다. 이 배율이 왜 하필 434.25인지는 5절에서 밝힙니다.

표가 말하는 것은 단위 선택이 곧 가중치 선택이라는 사실입니다. 우리가 아무 생각 없이 '걸음 수'라고 적는 순간, 우리는 이미 "걸음 수를 공부 시간보다 수백 배 무겁게 쳐 달라"고 컴퓨터에게 지시한 셈입니다. 의도한 적이 없는데도 말이지요.

그러니 할 일은 분명합니다. 모든 특성을 같은 자로 옮겨 놓고 재는 것. 그러면 단위가 무엇이었든 상관없어집니다.

3

같은 자로 옮기는 두 가지 방법

특성마다 제각각인 값의 크기를 서로 견줄 수 있게 옮기는 일을 정규화라고 합니다. 방법은 여러 가지지만 현장에서 쓰는 것은 사실상 둘입니다. 하나는 0과 1 사이로 눌러 넣기(min-max 정규화), 다른 하나는 평균 0·표준편차 1로 옮기기(z 점수 표준화)입니다.

① min-max 정규화 — 0과 1 사이로

생각은 간단합니다. 그 특성에서 가장 작은 값을 0, 가장 큰 값을 1로 만들고 나머지는 그 사이 어딘가에 놓는 것입니다. 식은 이렇습니다.

옮긴 값 = ( 원래 값 − 최솟값 ) ÷ ( 최댓값 − 최솟값 )

친구 셋의 값에서 기준을 뽑아 봅시다. 걸음 수의 최솟값은 나래의 2000, 최댓값은 가람의 5100입니다. 공부 시간의 최솟값은 나래의 2.1, 최댓값은 가람의 4.5입니다. 기준을 lo = [2000, 2.1], hi = [5100, 4.5]로 적어 둡니다.

다온의 걸음 수 4000을 옮겨 봅시다.

(4000 − 2000) ÷ (5100 − 2000) = 2000 ÷ 3100 = 0.6452

네 사람을 모두 옮기면 이렇게 됩니다.

사람원래 값걸음 수 → 0~1공부 시간 → 0~1
가람[5100, 4.5]1.01.0
나래[2000, 2.1]0.00.0
다온[4000, 3.0]0.64520.375
나[5000, 2.0]0.9677-0.0417
기준(lo·hi)은 친구 셋만으로 뽑았습니다. 왜 나를 빼야 하는지는 다음 절에서 다룹니다.
정규화 전 — 두 특성을 같은 수직선에 올리면 0 5100 나래 다온 나 가람 걸음 수 공부 시간 네 값(2.0 · 2.1 · 3.0 · 4.5)이 여기 한 점에 겹쳐 있다 정규화 후 — 둘 다 0~1 자로 0 1 나래 다온 나 가람 걸음 수 나래 다온 가람 나 공부 시간 나의 공부 시간만 0 왼쪽 바깥 (-0.0417)

그림 2. 정규화 전에는 공부 시간 네 값이 한 점에 겹쳐 서로 구별되지 않습니다. 거리 계산에서 이 특성이 사라지는 것이 바로 이 모습입니다. 정규화 뒤에는 두 특성 모두 눈금 전체를 씁니다.

0~1을 벗어난 값은 고장이 아니다

표에서 눈에 걸리는 값이 하나 있습니다. 나의 공부 시간이 -0.0417입니다. 0~1로 옮긴다고 해 놓고 음수가 나왔습니다. 코드가 잘못된 걸까요?

아닙니다. 기준을 친구 셋만으로 뽑았기 때문입니다. 친구 셋 중 공부 시간이 가장 적은 사람은 나래의 2.1시간이고, 그 값이 0이 되었습니다. 그런데 내 공부 시간은 2.0시간, 그보다도 적습니다. 그러니 0보다 작아지는 것이 당연합니다. 계산해 보면 (2.0 − 2.1) ÷ (4.5 − 2.1) = −0.1 ÷ 2.4 = −0.0417입니다.

내 공부 시간을 바꿔 가며 좌표가 어떻게 되는지 보면 규칙이 한눈에 보입니다.

나의 공부 시간옮긴 좌표범위이웃 순위
1.5시간-0.250 아래다온 < 나래 < 가람
2.0시간 (지금)-0.04170 아래다온 < 나래 < 가람
2.1시간0.0딱 0다온 < 나래 < 가람
2.2시간0.0417안쪽다온 < 가람 < 나래
4.5시간1.0딱 1가람 < 다온 < 나래
5.0시간1.20831 위가람 < 다온 < 나래
훈련에 쓰인 최솟값 2.1에서 정확히 0, 최댓값 4.5에서 정확히 1이 됩니다. 그 바깥은 음수거나 1보다 큽니다. 2등과 3등이 처음 자리를 바꾸는 자리는 2.2시간입니다.

정리하면 훈련에 없던 값이 0~1을 벗어나는 것은 규칙을 지켰다는 증거입니다. 만약 모든 새 값이 얌전히 0~1 안에 들어온다면, 오히려 기준을 잡을 때 그 값들을 함께 봤다는 뜻입니다. 다음 절의 주제가 바로 이것입니다.

옮긴 좌표로 다시 재면 — 답이 뒤집힌다

이제 옮긴 좌표로 거리를 다시 잽니다. 다온부터 손으로 해 봅시다. 나는 (0.9677, -0.0417), 다온은 (0.6452, 0.375)입니다.

다온 : √( 0.32252 + 0.41672 ) = √( 0.10401 + 0.17364 ) = 0.5269

세 사람을 모두 재고, 처음에 잰 값과 나란히 놓아 보겠습니다.

사람원래 값 거리순위정규화 거리순위자리 변화
가람100.03121등1.04223등▼ 2계단
다온1000.00052등0.52691등▲ 1계단
나래3000.03등0.96862등▲ 1계단
원래 값 가람 < 다온 < 나래 → 정규화 다온 < 나래 < 가람. 셋이 전부 자리를 바꿉니다.

1등이 3등으로, 2등이 1등으로, 3등이 2등으로 갔습니다. 우연히 두 명이 바뀐 정도가 아니라 순위표가 통째로 갈렸습니다. 그리고 이 뒤집힘은 특정한 세 사람 때문에 생긴 우연도 아닙니다. 다온을 빼고 둘만 남겨도 원래 값 가람 < 나래가 정규화 뒤 나래 < 가람으로 뒤집힙니다.

② z 점수 표준화 — 평균에서 얼마나 떨어졌나

두 번째 방법은 기준을 다르게 잡습니다. 최솟값·최댓값 대신 평균과 표준편차를 씁니다.

옮긴 값 = ( 원래 값 − 평균 ) ÷ 표준편차

옮긴 값의 뜻이 분명합니다. "평균에서 표준편차 몇 개만큼 떨어져 있는가"입니다. 0이면 딱 평균, +1이면 평균보다 표준편차 하나만큼 큼, −2면 평균보다 표준편차 둘만큼 작음. 이 값을 z 점수(z-score)라고 부릅니다.

친구 셋의 걸음 수 평균은 (5100+2000+4000)÷3 = 3700입니다. 평균에서 떨어진 거리를 제곱해 더하고 사람 수 3으로 나눈 뒤 뿌리를 씌우면 표준편차 1283.2251이 나옵니다. 공부 시간은 평균 3.2, 표준편차 0.9899입니다. 나의 걸음 수 5000을 옮기면 (5000 − 3700) ÷ 1283.2251 = 1.0131. 평균보다 표준편차 하나쯤 많이 걷는다는 뜻입니다.

사람걸음 수 z공부 시간 z나와의 거리순위
가람1.0911.31322.52663등
나래-1.3248-1.11122.342등
다온0.2338-0.2021.27581등
나1.0131-1.2122——
기준 : 평균 [3700.0, 3.2], 표준편차 [1283.2251, 0.9899]. 역시 친구 셋만으로 뽑았습니다.

거리 값은 min-max 때와 전혀 다릅니다(1.2758 대 0.5269). 그런데 순위는 똑같습니다 — 다온 < 나래 < 가람. 이것을 보고 "두 방법은 결국 같구나" 하고 넘어가면 안 됩니다. 친구가 셋뿐이고 값이 고르게 퍼져 있어서 같은 답이 나온 것뿐입니다. 5절에서 이 말을 직접 깨 보겠습니다.

min-max 정규화

범위가 보장된다. 훈련 데이터는 반드시 0~1 안에 들어옵니다. 이미지의 픽셀 값(0~255)처럼 최댓값·최솟값이 확실한 데이터에 잘 맞습니다.

이상치 하나에 통째로 찌그러진다. 최댓값 하나가 튀면 나머지 전부가 0 근처로 눌립니다. 4차시에서 본 이상치 문제가 여기서 다시 나타납니다.

z 점수 표준화

이상치에 덜 흔들린다. 두 값이 아니라 모든 값의 평균과 흩어짐으로 자를 만들기 때문에, 값 하나가 튀어도 자 전체가 무너지지는 않습니다.

범위가 정해지지 않는다. −3이 나올 수도 +8이 나올 수도 있습니다. "0~1 사이 값"을 요구하는 자리에는 그대로 넣을 수 없습니다.

💡 어느 쪽을 골라야 하나

"이럴 땐 무조건 이것"이라는 규칙은 없습니다. 실무에서는 둘 다 해 보고 결과가 좋은 쪽을 고릅니다. 다만 방향은 있습니다 — 값의 범위가 분명하고 이상치가 없으면 min-max, 이상치가 섞일 수 있으면 z 점수가 무난합니다. 이 차시에서 우리가 할 일은 고르는 법을 외우는 것이 아니라, 어느 쪽이 언제 무너지는지를 직접 보는 것입니다.

4

기준은 훈련 데이터에서만 뽑는다

앞 절에서 슬쩍 지나간 결정이 하나 있습니다. 최솟값·최댓값을 뽑을 때 친구 셋만 넣고 나는 뺐습니다. 왜 그랬을까요? 네 사람을 다 넣으면 계산이 더 자연스러워 보이는데 말입니다. 실제로 다 넣으면 나의 좌표가 [0.9677, 0.0]이 되어 아까 그 껄끄러운 음수도 사라집니다.

그런데 이것이 규칙 위반입니다. 오늘 지켜야 할 규칙을 한 줄로 적으면 이렇습니다.

⚠️ 지켜야 할 한 줄

변환 기준(최솟값·최댓값 또는 평균·표준편차)은 훈련 데이터에서만 뽑는다.
앞으로 들어올 데이터도 그 기준을 그대로 써서 옮긴다.

여기서 '나'는 훈련 데이터가 아닙니다. 나는 앞으로 판정을 받아야 할 새 사람입니다. 실제 상황을 떠올려 봅시다. 추천 시스템이 새 손님을 받을 때마다 "잠깐만요, 당신 값까지 넣어서 기준을 다시 계산할게요"라고 할 수는 없습니다. 기준은 서비스를 열기 전에 이미 정해져 있어야 합니다.

2차시에서 본 그 문제가 다시 나온다

2차시에서 우리는 데이터 누수를 배웠습니다. 급식 데이터의 '만족도' 열이 잔반량과 상관 −0.961로 가장 셌지만, 그 설문은 점심을 다 먹고 난 뒤 걷는 것이라 아침에 예측할 때는 쓸 수 없었지요. 알 수 없어야 할 정보가 모델에 새어 들어오는 것 — 그것이 데이터 누수(data leakage)입니다.

정규화 기준을 잡는 일에서도 똑같은 일이 일어납니다. 새로 들어올 값까지 넣어 최솟값·최댓값을 정하면, 아직 오지 않은 데이터를 미리 들여다본 것이 됩니다. 시험 문제를 먼저 보고 채점 기준을 만든 셈이지요. 누수는 알고리즘 안에서만 일어나는 사고가 아니라 데이터를 손질하는 단계에서 이미 일어날 수 있습니다.

정직하게 — 이 예에서는 답이 안 바뀐다

여기서 여러분을 겁주고 싶은 유혹이 있습니다. "규칙을 어기면 답이 틀려진다!"라고 쓰면 훨씬 극적이니까요. 그런데 실제로 돌려 보면 그렇지 않습니다. 결과를 그대로 보여 드립니다.

사람규칙을 지킨 거리
(기준 = 친구 셋)
규칙을 어긴 거리
(기준 = 친구 셋 + 나)
가람1.04221.0005
나래0.96860.9686
다온0.52690.5139
순위다온 < 나래 < 가람다온 < 나래 < 가람
기준이 lo = [2000, 2.1]에서 lo = [2000, 2.0]으로 바뀝니다. 내 공부 시간 2.0이 새 최솟값이 되기 때문입니다. hi는 그대로입니다.

거리 값은 셋 다 달라졌지만 순위는 같습니다. 이 작은 예에서는 규칙을 어겨도 답이 틀려지지 않았습니다. 사실 그대로 적어 둡니다. 그러면 규칙은 왜 지켜야 할까요? 두 가지 까닭이 있습니다.

  • 운이 좋았을 뿐입니다. 친구가 셋, 특성이 둘, 새 값이 하나인 아주 작은 판이라 기준이 조금 밀려도 순위가 버텼습니다. 훈련 데이터가 40개, 새 값이 15개인 8차시의 씨앗 데이터쯤 되면 이야기가 달라집니다. 어길 때마다 결과가 조금씩 흔들리고, 언제 뒤집힐지는 미리 알 수 없습니다.
  • 성적표를 믿을 수 없게 됩니다. 이것이 더 큰 문제입니다. 누수가 있는 모델은 시험 점수가 실제보다 좋게 나옵니다. 11차시에서 훈련 데이터와 테스트 데이터를 갈라 성능을 재는 법을 배우는데, 그때 잰 점수가 정직하려면 전처리 기준부터 훈련 데이터만 보고 정해져 있어야 합니다.

덧붙여, 규칙을 어기면 증거도 사라집니다. 규칙을 지켰을 때 나온 −0.0417이라는 값은 "새 값은 기준을 잡을 때 안 봤다"는 표시였습니다. 다 넣고 계산하면 그 값이 0.0으로 얌전해집니다. 겉보기에는 깔끔하지만, 깔끔해졌다는 사실 자체가 규칙을 어겼다는 표시입니다.

ℹ️ 15차시에서 갚아지는 이야기

오늘 만드는 정규화 함수를 15차시가 그대로 가져다 씁니다. 급식 잔반 240일 데이터로 신경망을 학습시키는 차시인데, 거기서 정규화를 빼고 돌려 보면 테스트 정확도가 47.2%로 내려갑니다. 늘 '적음'이라고만 답하는 기준선이 65.3%이니, 동전을 던지느니만 못한 결과입니다. 기온(1~34)이라는 큰 숫자가 체육 여부(0 또는 1)를 통째로 삼켜 버리기 때문입니다. 오늘 배우는 것은 형식적인 절차가 아니라, 그 차시가 굴러가기 위한 조건입니다.

5

답을 정하는 것은 숫자 하나다 — 환산율

지금까지 자를 여섯 가지로 바꿔 봤습니다. 원래 값, ×60(분), ×435, km 환산, 0~1 정규화, z 점수. 결과가 제각각이라 어지러웠을 것입니다. 그런데 이 모두를 숫자 하나로 정리할 수 있습니다.

거리 식을 다시 봅시다. 우리가 재는 것은 언제나 이 꼴입니다.

거리2 = (걸음 차)2 + ( r × 시간 차 )2

여기서 r은 "공부 1시간을 걸음 몇 걸음으로 칠 것인가"입니다. 이 차시에서 우리가 한 일은 전부 r을 정한 일이었습니다.

  • 원래 값 그대로 쓰면 r = 1입니다. 공부 1시간을 1걸음과 같게 친다는 뜻이지요. 이렇게 적어 놓고 보면 얼마나 이상한 결정이었는지 분명해집니다. 아무도 그렇게 결정한 적이 없지만, 표를 그렇게 적는 순간 그렇게 결정된 것입니다.
  • km로 바꿔 적으면 r = 1428.57입니다(1km = 1428.57걸음이므로).
  • min-max 정규화는 r = 1291.67입니다. 걸음 눈금 폭 3100을 시간 눈금 폭 2.4로 나눈 값이지요.
  • z 점수 표준화는 r = 1296.25입니다. 걸음 표준편차 1283.2251을 시간 표준편차 0.98995로 나눈 값입니다.

min-max와 z가 왜 같은 답을 냈는지, 이제 정확히 말할 수 있습니다. "데이터가 고르게 퍼져 있어서"가 아닙니다. 두 자가 고른 r이 1291.67과 1296.25로 0.36%밖에 차이 나지 않기 때문입니다. 거의 같은 자였던 것이지요.

r에 따라 답이 갈리는 자리 셋

r을 1부터 천천히 키우면 순위가 어떻게 될까요? 세 사람의 거리를 r에 대한 식으로 놓고 서로 같아지는 자리를 풀면 경계가 정확히 셋 나옵니다.

가람 = 다온 : r = 434.25 · 가람 = 나래 : r = 1200.29 · 다온 = 나래 : r = 2842.68
환산율 r = 공부 1시간을 걸음 몇 걸음으로 칠 것인가 (가로 눈금은 10배마다 한 칸) 가람 < 다온 < 나래 다온<가람<나래 다온<나래 <가람 나래< 다온<가람 434.25 1200.29 2842.68 1 10 100 1000 원래 값 r = 1 분 단위 (×60) r = 60 min-max 1291.67 · z 1296.25 두 자가 0.36% 차이로 겹친다 km 환산 r = 1428.57 초 (×3600)

그림 3. 이 차시에서 바꿔 든 자가 전부 이 눈금선 위의 점 하나입니다. 정규화 두 방법과 km 환산이 같은 칸에 들어가 같은 답을 냈고, 원래 값(r = 1)과 분 단위(r = 60)는 맨 왼쪽 칸에 들어가 서로 같은 답을 냈습니다.

이 눈금선이 알려 주는 것이 하나 더 있습니다. 원래 값과 분 단위가 같은 칸에 있다는 사실입니다. 2절에서 ×60으로 순위가 안 바뀌었던 까닭이 이것입니다 — 60은 434.25에 한참 못 미칩니다. 그리고 초 단위(×3600)는 맨 오른쪽 칸이라 정규화와도 다른 답을 냅니다.

이상치 한 명이 두 자를 벌려 놓는다

친구 라온이 들어옵니다. 공부 시간은 3.1시간으로 평범한데, 만보기가 고장 나 걸음 수가 터무니없이 크게 찍힙니다. 4차시에서 본 걸음 수 41000(자전거 대회)이나 키 1750.0(자릿수 오타) 같은 값이지요.

라온이 들어오면 두 자가 고르는 r이 어떻게 되는지 봅시다.

라온의 걸음 수min-max의 rz 점수의 r두 r의 차이답이 갈리나
라온 없음1291.671296.250.36%같다
6,0001666.671738.414.30%같다
8,7182799.172843.101.57%갈린다
8,8232842.922890.351.67%같다
40,00015833.3318356.6415.94%같다
48,45219355.0022611.4916.83%갈린다
이상치가 커질수록 두 자가 벌어집니다. 그런데 차이가 크다고 답이 갈리는 것은 아닙니다 — 40,000에서 15.94%나 벌어졌는데도 답은 같습니다.

8,718이라는 자리를 봅시다. 두 r이 2799.17과 2843.10입니다. 경계 하나가 2842.68에 있었지요. z만 그 선을 넘었고 min-max는 아직 못 넘었습니다. 그래서 답이 갈립니다. 차이가 1.57%밖에 안 나는데도 갈리는 까닭이 이것입니다.

즉 두 r의 차이가 크냐 작냐가 아니라, 그 사이에 경계가 끼었느냐가 답을 정합니다. 8,823까지 105걸음만 더 올리면 min-max도 선을 넘어 다시 같은 답이 됩니다. 폭 105걸음짜리 틈이지요. 손으로 찍어서는 절대 못 찾고 기계에게 훑게 해야 보이는 자리입니다. 잠시 뒤 시뮬레이터로 여러분이 직접 찾아볼 것입니다.

정직하게 — 흔히 오해하는 것 하나

"min-max는 이상치에 짓눌리고 z 점수는 덜 흔들린다"는 말을 자주 듣습니다. 절반만 맞는 말이라 짚고 갑니다. 라온이 40,000걸음일 때 걸음 수 좌표를 보세요.

사람원래 걸음 수min-maxz 점수
가람51000.0816-0.4871
나래20000.0-0.6838
다온40000.0526-0.5569
라온400001.01.7277
나50000.0789-0.4934

min-max 쪽은 라온을 뺀 넷이 0.00 ~ 0.09에 뭉쳤습니다. 눈금 전체가 1이니 8.16%를 씁니다. z 쪽은 -0.68 ~ -0.49에 있고, 눈금 전체가 -0.68에서 1.73까지 2.41이니 역시 8.16%입니다. 완전히 같습니다. 두 방법 모두 나누기 하나로 축을 옮기는 1차 변환이라, 한 축 안에서 서로 얼마나 떨어져 있는지는 바뀔 수가 없습니다.

⚠️ 그러면 무엇이 다른가

달라지는 것은 두 축 사이의 환산율뿐입니다. 라온이 들어오면 min-max는 r을 1291.67 → 15833.33으로, z는 1296.25 → 18356.64로 밉니다. 두 방법 다 이상치에 자를 빼앗깁니다. 미는 정도가 다를 뿐이지요. "z를 쓰면 이상치가 해결된다"가 아니라 "이상치는 그 전에 걸러 내야 한다"가 맞는 결론입니다. 4차시에서 이상치를 찾는 데 한 시간을 쓴 까닭이 여기 있습니다.

⚠️ 특성 하나가 모두 같은 값이면 프로그램이 멈춘다

친구 셋의 공부 시간을 전부 3.0으로 맞춰 보면 어떻게 될까요? 최솟값과 최댓값이 같아지므로 hi − lo = 0, 곧 0으로 나누기가 되어 ZeroDivisionError로 프로그램이 멈춥니다. z 점수도 표준편차가 0이 되어 똑같이 멈춥니다. 고쳐서 막을 수도 있지만, 애초에 모든 값이 같은 특성은 정보가 0이므로 정규화하기 전에 빼는 것이 맞습니다. 6차시에서 특성을 고르는 눈을 다룹니다.

그런데 정규화가 필요 없는 알고리즘도 있다

여기까지 읽고 "그럼 늘 정규화부터 하면 되겠구나"라고 결론 내리기 쉽습니다. 그런데 그렇지 않습니다. 무조건 해야 하는 전처리는 없습니다.

아주 단순한 판정 규칙을 하나 만들어 봅시다. "걸음 수가 3000보다 많으면 많이 걷는 쪽." 이 규칙을 km 자로 옮겨 적으면 "2.1km보다 많으면"이 됩니다. 결과를 견주면 이렇습니다.

사람걸음 수 자로 판정km 자로 판정
가람많이 걷는 쪽많이 걷는 쪽
나래적게 걷는 쪽적게 걷는 쪽
다온많이 걷는 쪽많이 걷는 쪽
나많이 걷는 쪽많이 걷는 쪽
네 사람 모두 판정이 같습니다. 자를 바꿔도 답이 안 바뀝니다.

까닭은 분명합니다. 이 규칙은 '크다/작다'만 따집니다. 단위를 바꿔도 누가 더 큰지는 그대로이므로 판정도 그대로입니다. 이런 성질을 가진 대표적인 알고리즘이 결정 트리입니다. 결정 트리는 "이 특성이 이 값보다 큰가"라는 질문만 던지기 때문에 정규화가 필요 없습니다. 환산율 r이라는 것이 아예 등장하지 않으니까요.

정규화가 꼭 필요한 것은 값의 크기를 직접 계산에 넣는 알고리즘입니다.

  • 거리를 재는 알고리즘 — 8차시의 k-최근접 이웃, 10차시의 k-평균 군집. 오늘 본 그대로, 큰 숫자가 거리를 독점합니다. 8차시에서 씨앗의 무게를 mg로 바꾸고 정규화를 끄면 정확도가 46.7~66.7%로 무너지고, 예측이 '무게 하나만 쓴 것'과 15개 전부 같아집니다.
  • 가중치를 곱하는 알고리즘 — 9차시의 회귀, 13~15차시의 퍼셉트론과 신경망. 입력 값의 크기가 제각각이면 학습이 한쪽으로 쏠려 잘 수렴하지 않습니다. 9차시에서 공부 시간을 분 단위로 바꿔 보면 학습률의 문턱이 3600배(= 602) 작아져야 겨우 돌아갑니다.

정리하면 "정규화를 할까 말까"는 데이터가 아니라 알고리즘이 정합니다. 쓸 알고리즘을 먼저 정하고, 그것이 크기를 보는 종류인지 순서만 보는 종류인지 따지면 됩니다.

💻

손으로 ① — 자를 바꿔 가며 이웃을 다시 세운다

지금까지 읽은 것을 화면에서 직접 움직여 봅시다. 가로축이 걸음 수, 세로축이 공부 시간인 산점도입니다. 분홍색 점이 나이고, 나에게서 가까운 이웃 셋까지 선이 이어집니다. 자를 바꾸면 점들이 자리를 옮기고 선이 다시 그어집니다.

📐 같은 자로 재기 — 정규화 산점도 INTERACTIVE

자를 바꾸면 점들이 새 자리로 옮겨 가고 순위표가 다시 매겨집니다. 1등은 초록, 2등은 하늘색, 3등은 보라색 선입니다. 이웃에 못 든 사람은 회색 점선입니다. 나의 걸음 수·공부 시간을 밀어 순위가 바뀌는 문턱을 찾고, 라온을 켜서 min-max와 z 점수가 서로 다른 답을 내는 자리를 찾아보세요. 표의 값은 이 차시의 파이썬 코드가 내놓는 값과 소수 넷째 자리까지 같습니다.

5000 2.0
사람옮긴 좌표나와의 거리순위원래 값 대비이웃 셋
지금 쓰는 자원래 값
환산율 r (1시간 = ? 걸음)1
나의 옮긴 좌표5000, 2.0
min-max와 z가—
[안내] 자를 바꿔 보세요.
✍️ 공책에 적을 것 — 세 가지 과제

아래 표를 공책에 옮겨 그리고, 시뮬레이터를 돌려 채우세요. 확인 문제에서 이 값을 씁니다.

과제 ① 네 가지 자로 재 보기 — 나는 5000걸음·2.0시간, 라온은 끄고

자환산율 r1등2등3등
원래 값
km로 바꿔 적기
0~1 정규화
z 점수

과제 ② 문턱 찾기 — 자를 '0~1 정규화'에 두고, 나의 공부 시간을 2.0부터 0.1씩 올리며 순위가 처음 달라지는 값을 찾으세요.

순위가 처음 달라지는 나의 공부 시간그때의 순위그 직전 순위

과제 ③ 반례 만들기 — 라온을 켜고, min-max와 z 점수가 서로 다른 답을 내는 라온의 걸음 수를 찾으세요. 두 군데 있습니다. 하나는 아주 좁습니다(폭 105걸음).

찾은 자리라온의 걸음 수min-max 순위z 점수 순위
좁은 구간
넓은 구간

힌트 ③ : 숫자 칸에 값을 바로 입력할 수 있습니다. 8,000 근처와 48,000 근처를 훑어보세요. min-max와 z가 칸이 다르다로 바뀌면 찾은 것입니다.

💡 '기준에 나까지 넣기'를 켜면

4절에서 다룬 규칙 위반을 화면에서 바로 볼 수 있습니다. 켜는 순간 나의 좌표가 0~1 안으로 얌전히 들어오고, 세 사람의 거리 값이 모두 조금씩 달라집니다. 순위는 그대로일 것입니다. 규칙을 어겨도 이 예에서는 답이 안 바뀐다는 것을 직접 확인해 두세요. 그래도 어기면 안 되는 까닭은 4절에 적어 두었습니다.

💻

손으로 ② — 다섯 개의 부품을 만든다

시뮬레이터가 화면 뒤에서 하고 있던 계산을 직접 짜 봅니다. 오늘 만들 함수는 다섯 개입니다.

함수하는 일넣는 것나오는 것
dist(a, b)두 점 사이 유클리드 거리점 둘거리 하나
minmax_fit(rows)0~1 기준을 뽑는다훈련 데이터만최솟값·최댓값
minmax_apply(v, lo, hi)그 기준으로 점 하나를 옮긴다점 하나 + 기준옮긴 좌표
zscore_fit(rows)z 기준을 뽑는다훈련 데이터만평균·표준편차
zscore_apply(v, mu, sd)그 기준으로 점 하나를 옮긴다점 하나 + 기준옮긴 좌표
이 다섯 개는 오늘로 끝나지 않습니다. 8차시 k-최근접 이웃, 11차시 과적합 실험, 15차시 신경망이 이 함수들을 이름도 식도 고치지 않고 그대로 다시 싣습니다 (오늘의 빈칸 안내 주석만 뺍니다 — 그 차시에서는 이미 채워진 부품이니까요). 파이썬은 차시마다 새로 뜨므로 "앞 차시에서 만들었다"가 통하지 않기 때문입니다.
💡 dist를 2차원 전용으로 짜지 마세요

(a[0]-b[0])**2 + (a[1]-b[1])**2로 짜도 오늘은 잘 돌아갑니다. 그런데 15차시의 급식 데이터는 특성이 네 개입니다. 그때 이 함수를 다시 실으면 뒤의 두 특성이 조용히 무시됩니다 — 오류도 안 납니다. len(a)만큼 도는 꼴로 짜 두면 특성이 몇 개든 그대로 돕니다.

빈칸 넷을 채우세요

아래 코드에 ?????가 다섯 자리 있습니다. 채울 것은 네 종류입니다.

  • 빈칸 ① dist() — 각 특성의 차를 제곱해 모두 더하고, 마지막에 ** 0.5로 뿌리를 씌웁니다.
  • 빈칸 ② minmax_apply() — 최솟값을 빼고, 최댓값과 최솟값의 차로 나눕니다.
  • 빈칸 ③ zscore_apply() — 평균을 빼고, 표준편차로 나눕니다.
  • 빈칸 ④ minmax_fit(?????)와 zscore_fit(?????) — 여기에 무엇을 넣을지가 오늘의 핵심입니다. 친구 셋만입니까, 친구 셋에 나까지입니까? 답을 고르는 것이 아니라 4절의 규칙을 지킬지 말지를 판단하는 자리입니다. 두 곳에 같은 답이 들어갑니다.
⚠️ 아래 코드 【3】에 답이 한 줄 보입니다

【3】은 일부러 규칙을 어겨 보는 대조군이라 minmax_fit(list(FRIENDS.values()) + [ME])가 그대로 적혀 있습니다. 빈칸 ④에 그것을 그대로 베껴 넣을 수도 있습니다. 그러면 이 차시에서 배울 것이 하나도 남지 않습니다. 【3】은 채운 뒤에 읽으세요.

ℹ️ 실행이 안 되면

SyntaxError: invalid syntax가 나오고 ?????가 가리켜진다면 아직 안 채운 빈칸이 남은 것입니다. 오류 메시지의 줄 번호를 보고 그 줄을 고치세요. 오류 메시지는 잔소리가 아니라 안내문입니다. 답을 맞게 채웠다면 출력이 【1】부터 【5】까지 나옵니다.

이어서 — 라온을 넣고 두 자를 갈라 보기

두 번째 상자는 5절의 실험을 그대로 돌립니다. 라온의 걸음 수를 6,000부터 4,000씩 올려 가며 두 방법의 답을 견주고, 갈리는 자리를 1걸음 단위까지 좁혀 찾습니다. 6,000부터 80,000까지 100걸음 간격으로 741자리를 훑은 뒤, 답이 바뀐 곳만 1걸음씩 다시 재는 방식입니다.

⚠️ 반복에는 반드시 끝이 있어야 합니다

코드의 탐색 반복은 range(6000, 80001, 100)처럼 끝이 정해진 범위를 돕니다. "답이 나올 때까지"라는 뜻으로 while True를 쓰면 브라우저 안의 파이썬은 화면과 같은 자리에서 돌기 때문에 탭이 그대로 멈춰 버립니다. 고쳐서 실험할 때도 범위의 끝은 남겨 두세요.

🔧 코드를 부숴 보기 — 넷 중 둘 이상
  • 공부 시간에 배율을 곱해 보기. FRIENDS와 ME의 두 번째 값에 60을 곱하면 순위가 바뀔까요? 435를 곱하면? 3600을 곱하면? 답을 예상하고 나서 돌려 보세요.
  • 거리 식을 바꿔 보기. dist를 제곱·뿌리 대신 sum(abs(a[i]-b[i]) for i in range(len(a)))로 바꿔 보세요(맨해튼 거리). 원래 값 순위도 정규화 순위도 하나도 안 바뀝니다. 왜 그럴까요?
  • 친구를 하나 지워 보기. FRIENDS에서 다온을 지우고 둘만 남겨 보세요. 뒤집힘이 여전히 일어납니까?
  • 일부러 터뜨려 보기. 친구 셋의 공부 시간을 전부 3.0으로 바꾸면 어떤 오류가 납니까? 오류 메시지의 마지막 줄을 공책에 적어 두세요.
📖

정리 — 오늘 얻은 것

오늘 한 일을 다섯 줄로 줄이면 이렇습니다.

  • 비슷함은 거리로 잰다. 유클리드 거리는 각 특성의 차를 제곱해 더하고 뿌리를 씌운 값입니다. 특성이 몇 개든 항을 더 붙이면 됩니다.
  • 단위가 큰 특성이 판정을 독점한다. 걸음 차의 제곱 9,000,000이 공부 차의 제곱 0.01을 삼켰습니다. 특성 두 개로 쟀다고 생각했지만 실제로는 하나만 쓰였습니다.
  • 정규화는 모든 특성을 같은 자로 옮기는 일이다. 최솟값·최댓값으로 0~1에 넣는 min-max, 평균·표준편차로 옮기는 z 점수. 어느 쪽이든 정하는 것은 환산율 하나입니다.
  • 기준은 훈련 데이터에서만 뽑는다. 새 값이 0~1을 벗어나는 것은 고장이 아니라 규칙을 지켰다는 증거입니다. 새 값까지 넣어 기준을 잡으면 전처리 단계의 데이터 누수입니다.
  • 무조건 하는 전처리는 없다. 크기를 계산에 넣는 알고리즘(거리·가중치)에만 필요하고, 크다·작다만 따지는 알고리즘(결정 트리)에는 필요 없습니다.
오늘의 숫자값어디서 나왔나
원래 값 거리가람 100.0312 · 다온 1000.0005 · 나래 3000.0실습 ① 【1】
정규화 거리다온 0.5269 · 나래 0.9686 · 가람 1.0422실습 ① 【2】
나의 옮긴 좌표[0.9677, -0.0417]실습 ① 【2】
환산율 r원래 값 1 · min-max 1291.67 · z 1296.25 · km 1428.575절 · 시뮬레이터
순위가 갈리는 경계434.25 · 1200.29 · 2842.685절 그림 3
두 방법이 갈리는 라온의 걸음 수8,718 ~ 8,822 · 48,452 이상실습 ② 【6】
ℹ️ 오늘 만든 부품이 가는 곳

dist · minmax_fit · minmax_apply · zscore_fit · zscore_apply 다섯 개는 이 단원의 뒷부분에서 계속 쓰입니다.

  • 8차시 k-최근접 이웃 — 다섯 개 전부 그대로 싣고, 씨앗 55알을 품종 둘로 나눕니다.
  • 11차시 과적합 실험 — dist를 싣고, 훈련 데이터와 테스트 데이터를 가릅니다.
  • 15차시 신경망으로 급식 잔반 맞히기 — dist·minmax_fit·minmax_apply를 싣고 특성 네 개에 씁니다.
✅

확인 문제

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

1. min-max 정규화와 z 점수 표준화의 식을 각각 쓰시오. 그리고 "이상치가 섞였을 때 z 점수가 덜 망가진다"는 흔한 설명이 어디까지 맞고 어디서부터 부정확한지 오늘 확인한 사실로 설명하시오.
📖 모범 답안

식. min-max는 (값 − 최솟값) ÷ (최댓값 − 최솟값), z 점수는 (값 − 평균) ÷ 표준편차입니다.

맞는 부분. min-max의 기준은 오직 두 값(최솟값·최댓값)이라 이상치 하나가 그 두 값 중 하나를 통째로 정해 버립니다. z 점수는 모든 값의 평균과 표준편차를 쓰므로 한 값의 영향이 조금 희석됩니다.

부정확한 부분. "덜 망가진다"를 한 특성 안에서 값들이 더 잘 구별된다는 뜻으로 읽으면 틀립니다. 라온이 40,000걸음일 때 나머지 넷은 min-max에서 눈금 전체의 8.16%를 쓰고, z 점수에서도 정확히 8.16%를 씁니다. 두 방법 다 나누기 하나로 축을 옮기는 1차 변환이라 한 축 안의 상대 간격은 바뀔 수가 없습니다. 실제로 달라지는 것은 두 축 사이의 환산율뿐입니다(min-max 15833.33 대 z 18356.64). 두 방법 다 이상치에 자를 빼앗기며, 미는 정도만 다릅니다. 그러므로 옳은 결론은 "z를 쓰면 된다"가 아니라 "이상치는 정규화 전에 걸러 내야 한다"입니다.

2. 실습 ①을 돌린 결과를 보고 답하시오. 정규화 전과 후에 '나와 가장 비슷한 친구'가 각각 누구였는가? 세 사람의 거리 값을 각각 쓰고, 왜 순위가 통째로 뒤집혔는지 거리 식의 항으로 설명하시오.
📖 모범 답안

정규화 전. 가람 100.0312 · 다온 1000.0005 · 나래 3000.0 → 순위 가람 < 다온 < 나래, 가장 비슷한 친구는 가람.

정규화 후. 다온 0.5269 · 나래 0.9686 · 가람 1.0422 → 순위 다온 < 나래 < 가람, 가장 비슷한 친구는 다온. 1등이 3등으로, 2등이 1등으로, 3등이 2등으로 — 셋이 전부 자리를 바꿉니다.

까닭. 정규화 전에는 거리 안의 두 항이 (걸음 차)2 = 10000 / 9000000 / 1000000 과 (공부 차)2 = 6.25 / 0.01 / 1.0 이었습니다. 뒤의 항이 앞의 항보다 나래에서 9억 배, 다온에서 100만 배, 가람에서도 1,600배 작습니다. 더하는 순간 뒤의 항이 사라집니다. 그래서 걸음 수만으로 순위가 정해졌고, 걸음 수가 가장 가까운 가람이 1등이었습니다. 정규화 뒤에는 두 항이 모두 0~1 눈금에서 나오므로 공부 시간이 계산에 참여합니다. 공부 시간이 나와 2.5시간이나 차이 나는 가람은 그때 3등으로 밀립니다.

3. 시뮬레이터의 과제 ③에서 찾은 값을 쓰시오. min-max와 z 점수가 서로 다른 답을 내는 라온의 걸음 수는 어느 구간이었는가? 두 방법의 환산율이 15.94%나 차이 나는 40,000걸음에서는 오히려 답이 같았다. 그런데 1.57%밖에 차이 나지 않는 8,718걸음에서는 답이 갈렸다. 왜 그런가?
📖 모범 답안

찾은 구간 둘. ① 8,718 ~ 8,822(폭 105걸음) — min-max 다온 < 나래 < 라온 < 가람 / z 나래 < 다온 < 라온 < 가람. ② 48,452 이상 — min-max 나래 < 다온 < 가람 < 라온 / z 나래 < 다온 < 라온 < 가람.

까닭. 답을 정하는 것은 두 환산율의 차이가 아니라 그 사이에 순위가 바뀌는 경계가 끼었는지입니다. 8,718걸음에서 min-max의 r은 2799.17, z의 r은 2843.10인데, '다온 = 나래' 경계가 2842.68에 있습니다. z만 그 선을 넘었고 min-max는 아직 못 넘었으므로 두 답이 갈립니다. 8,823걸음이 되면 min-max의 r도 2842.92로 선을 넘어 다시 같은 답이 됩니다 — 그래서 폭이 105걸음입니다. 반대로 40,000걸음에서는 두 r(15833.33과 18356.64)이 크게 벌어졌지만 그 사이에 경계가 하나도 없어 답이 같습니다.

4. 앞으로 판정할 새 데이터의 값까지 넣어 정규화 기준을 잡으면 무엇이 잘못되는가? 그런데 오늘 실습에서는 규칙을 어겨도 순위가 바뀌지 않았다. 그렇다면 이 규칙은 왜 지켜야 하는가?
📖 모범 답안

무엇이 잘못되나. 아직 오지 않았어야 할 데이터를 미리 들여다본 것이 됩니다. 2차시에서 배운 데이터 누수가 알고리즘이 아니라 전처리 단계에서 일어난 것이지요. 실제 서비스에서는 새 손님이 올 때마다 기준을 다시 계산할 수 없으므로, 기준은 문을 열기 전에 이미 정해져 있어야 합니다.

그래도 지켜야 하는 까닭 둘. ① 오늘 순위가 버틴 것은 운입니다. 친구 셋·특성 둘·새 값 하나인 아주 작은 판이라 기준이 조금 밀려도 결과가 견뎠습니다. 데이터가 커지면 언제 뒤집힐지 미리 알 수 없습니다. ② 더 중요한 것은 성적표를 믿을 수 없게 된다는 점입니다. 누수가 있는 모델은 시험 점수가 실제보다 좋게 나옵니다. 11차시에서 훈련 데이터와 테스트 데이터를 갈라 성능을 잴 때, 그 점수가 정직하려면 전처리 기준부터 훈련 데이터만 보고 정해져 있어야 합니다.

덧붙여, 규칙을 어기면 나의 좌표가 −0.0417에서 0.0으로 바뀌어 증거도 사라집니다. 깔끔해 보이는 것 자체가 규칙을 어겼다는 표시입니다.

5. 키(150~185cm)와 몸무게(40~90kg) 두 특성만 쓰는 모델이 있다. 이 경우에도 정규화가 필요할까? 판단하고 근거를 대시오. (힌트: 어떤 알고리즘을 쓸 것인지부터 정하고, 환산율이 얼마나 달라지는지 따져 보시오.)
📖 모범 답안

먼저 알고리즘을 정해야 합니다. 결정 트리처럼 "이 값보다 큰가"만 묻는 알고리즘이라면 정규화가 필요 없습니다. 환산율이라는 것이 아예 등장하지 않기 때문입니다. 거리를 재는 k-최근접 이웃이나 가중치를 곱하는 신경망이라면 따져 봐야 합니다.

거리를 재는 경우. 원래 값으로 재면 "몸무게 1kg 차이 = 키 1cm 차이"로 치는 셈입니다. 정규화하면 키의 범위 35, 몸무게의 범위 50이 둘 다 1이 되므로 "몸무게 1kg 차이 = 키 0.7cm 차이"로 바뀝니다. 두 결정이 크게 다르지 않습니다 — 걸음 수와 공부 시간처럼 수백 배씩 벌어지지 않습니다.

그러므로 답은 "효과가 작다"이지 "필요 없다"가 아닙니다. 두 특성의 범위가 비슷할 때는 정규화를 해도 결과가 크게 안 바뀝니다. 그래도 해 두는 편이 낫습니다 — 나중에 특성을 하나 더 붙일 때 (예를 들어 하루 걸음 수) 그 순간 범위가 수천 배로 벌어지기 때문입니다. 정규화는 지금 필요해서가 아니라 나중에 안전하려고 해 두는 절차이기도 합니다.

6. 정규화를 해도 한 특성 안에서 '누가 더 큰지'는 그대로 유지된다. 가람은 정규화 전에도 후에도 가장 많이 걷는 사람이다. 그런데도 '가장 비슷한 친구'는 바뀌었다. 그렇다면 정규화로 달라진 것은 정확히 무엇인가?
📖 모범 답안

달라진 것은 특성들 사이의 환산율입니다. min-max든 z든 한 축을 상수로 나누는 1차 변환이라, 같은 축 안에서의 순서와 상대 간격은 전혀 바뀌지 않습니다. 달라지는 것은 "걸음 1걸음의 무게"와 "공부 1시간의 무게"의 비, 곧 환산율 r뿐입니다. 원래 값에서는 r = 1이었고 정규화 뒤에는 r = 1291.67이 되었습니다.

그래서 어떤 계산이 영향을 받는지도 정해집니다. 여러 특성을 한데 섞어 쓰는 계산 — 거리를 재거나 가중치를 곱해 더하는 계산 — 만 결과가 달라집니다. 한 특성만 보고 크다·작다를 따지는 계산은 영향을 받지 않습니다. 결정 트리가 정규화에 무관한 까닭이 이것이고, k-최근접 이웃과 신경망이 정규화를 반드시 필요로 하는 까닭도 이것입니다.

한 걸음 더 나가면 이렇게도 말할 수 있습니다. 정규화는 데이터를 고치는 일이 아니라, 특성들 사이의 무게를 정하는 결정입니다. 그 결정을 안 하면 '단위를 무엇으로 적었는가'라는 우연이 대신 결정해 버립니다.

🔁 되돌아보기

오늘 거리 하나로 '비슷함'을 재고, 단위 때문에 답이 통째로 뒤집히는 것을 직접 일으켜 봤습니다. 다섯 개의 부품(dist·minmax_fit·minmax_apply·zscore_fit·zscore_apply)도 손으로 만들었습니다. 다음 시간에는 특성이 여럿일 때 무엇을 쓰고 무엇을 버릴지 고르는 눈을 기릅니다 — 상관계수 하나만 보고 특성을 버리면 왜 안 되는지, 실제 데이터로 확인합니다.

🔎

더 알아보기

단위를 빠뜨린 숫자가 부른 사고, 우리 곁의 z 점수, 이상치에 버티는 또 하나의 자

붉은 화성 표면 위를 도는 탐사선을 그린 그림. 긴 태양 전지판 한 장과 접시 안테나, 금박을 두른 상자 모양 몸체가 보인다
역사

숫자만 건넸다가 잃은 탐사선 — 화성 기후 궤도선

1998년 12월에 발사된 미국 항공우주국(NASA)의 화성 기후 궤도선(Mars Climate Orbiter)은 1999년 9월 23일 화성 궤도에 들어서던 중 연락이 끊겼습니다. 계획보다 훨씬 낮게 화성 대기로 파고들었던 것입니다.

원인은 오늘 우리가 본 것과 같은 종류의 실수였습니다. 탐사선을 만든 회사의 지상 프로그램은 추진기가 낸 힘의 효과를 파운드힘·초 단위로 계산해 넘겼는데, 항법을 맡은 제트추진연구소(JPL)의 프로그램은 약속된 대로 그 숫자를 뉴턴·초로 읽었습니다. 1파운드힘은 약 4.45뉴턴이라 매번 효과가 네 배 넘게 작게 잡혔고, 그 오차가 몇 달 동안 쌓여 궤도가 어긋났습니다.

두 프로그램 사이를 오간 것은 단위가 떨어져 나간 숫자뿐이었습니다. [5000, 2.0]만 받은 우리 거리 함수가 무엇이 걸음이고 무엇이 시간인지 몰랐던 것처럼요. 그래서 데이터를 다룰 때는 숫자와 함께 단위와 기준을 기록해 두고, 받은 쪽에서 그 기록을 확인하는 절차를 둡니다. 정규화의 lo·hi를 따로 저장해 두는 것도 같은 까닭입니다.

사진: 화성 기후 궤도선 상상도 · 출처: NASA/JPL, Wikimedia Commons (Public domain)

같은 원점수 80점, 과목마다 다른 자 (가상의 예) 과목 A 평균 60 표준편차 10 z = (80 − 60) ÷ 10 z = 2 100 + 20 × 2 140 과목 B 평균 70 표준편차 20 z = (80 − 70) ÷ 20 z = 0.5 100 + 20 × 0.5 110 원점수와 기준 오늘 배운 z 점수 표준점수 국어·수학: 100 + 20 × z · 탐구: 50 + 10 × z
현장

수능 성적표의 표준점수 — 우리 곁의 z 점수

대학수학능력시험 성적표에는 표준점수가 적힙니다. 국어·수학의 표준점수는 그 과목 응시자 전체의 평균과 표준편차로 z 점수를 구한 뒤 20을 곱하고 100을 더한 값이고, 탐구 과목은 10을 곱하고 50을 더한 값입니다. 오늘 배운 표준화에 보기 좋은 눈금을 한 번 더 씌운 것이지요.

그림처럼 원점수가 똑같이 80점이어도, 평균이 낮고 점수가 촘촘히 모인 과목에서는 z가 커서 표준점수가 높고, 평균이 높고 점수가 넓게 퍼진 과목에서는 낮습니다. 과목마다 난이도가 달라 원점수를 그대로 견줄 수 없으니 모든 과목을 같은 자로 옮겨 견주는 것입니다. 걸음 수와 공부 시간을 같은 자로 옮긴 오늘의 일과 뜻이 같습니다.

기준을 어디서 뽑는지도 눈여겨보세요. 표준점수의 평균과 표준편차는 그해 그 과목을 본 사람들에게서 나옵니다. 그래서 같은 80점이라도 해마다 표준점수가 달라집니다. 기준이 곧 자이고, 자를 만든 데이터가 바뀌면 값도 바뀝니다.

라온이 더 튀면 — 평균은 끌려가고 중앙값은 버틴다 라온 40,000걸음 라온 평균 12,775 중앙값 4,550 라온 80,000걸음 라온 평균 22,775 중앙값 4,550 0 80,000 중앙값은 가운데 두 사람(4,000·5,100)만 본다. 라온이 얼마나 튀든 기준점이 움직이지 않는다.
원리 더 깊이

이상치에 버티는 자 — 중앙값과 가운데 절반의 폭

5절에서 min-max도 z 점수도 라온 한 명에게 자를 빼앗긴다는 것을 확인했습니다. 까닭은 두 방법이 기준으로 삼는 값 — 최솟값·최댓값, 평균·표준편차 — 이 끝에 있는 값 하나에 끌려가기 때문입니다. 그림처럼 라온이 40,000걸음에서 80,000걸음이 되면 평균은 12,775에서 22,775로 밀려납니다.

그래서 끌려가지 않는 기준을 쓰는 방법이 있습니다. 평균 대신 중앙값을 빼고, 표준편차 대신 값들을 줄 세웠을 때 가운데 절반이 차지하는 폭(사분위 범위)으로 나누는 것입니다. 네 사람의 중앙값은 가운데 두 사람 4,000과 5,100의 평균인 4,550이라, 라온이 얼마나 크든 그대로입니다. 파이썬 기계학습 도구 scikit-learn에는 이 방법이 RobustScaler라는 이름으로 들어 있습니다.

그렇다고 이것이 이상치 문제의 답은 아닙니다. 자가 버텨 줘도 라온의 점은 여전히 멀리 떨어져 있고, 만보기가 고장 나 찍힌 값이라면 그 값은 처음부터 틀린 데이터입니다. 본문의 결론 그대로, 이상치는 정규화 전에 찾아서 걸러 내는 것이 먼저이고, 튼튼한 자는 그다음에 고를 수 있는 도구입니다.