단원 홈
2단원 · 15차시

내 데이터로 학습시키고 성능을 잰다
만든 것을 처음으로 평가한다

14차시의 신경망은 XOR 네 점만 외웠습니다. 오늘은 그 코드를 한 글자도 고치지 않고 우리 학교 급식 기록 240일치를 먹입니다. 그리고 처음으로 묻습니다 — 이것은 얼마나 잘하는가. 그 ‘얼마나’를 무엇으로 재야 할까요?

성취기준 12인기02-06 성취기준 12인기02-05
급식 잔반 240일훈련 / 테스트 네 칸(혼동행렬)정밀도 · 재현율 기준선과적합재현성
🎯 학습 목표
  • 14차시의 train()을 고치지 않고 실생활 데이터에 붙여, 훈련 데이터와 테스트 데이터로 나누어 학습시킬 수 있다.
  • 학습 결과를 12차시의 네 칸(혼동행렬)으로 재고, 정확도 하나만 보고했을 때 무엇이 감춰지는지 내가 얻은 숫자로 설명할 수 있다.
  • 은닉 노드 수 · 학습률 · 반복 수를 바꿔 가며 훈련 성능과 테스트 성능이 갈라지는 지점을 직접 찾아내고, 내가 만든 것과 실제로 쓰이는 딥러닝의 거리를 세 가지로 말할 수 있다.
🤔

여는 장면 — 네 점을 외운 그물에게 진짜 일을 시키면

지난 시간 우리가 만든 신경망은 시험을 아주 잘 봤습니다. 손실이 0.000383까지 내려갔고, 네 점을 전부 맞혔지요. 그런데 그 시험은 문제가 넷뿐이었습니다. (0,0) (0,1) (1,0) (1,1) — 그리고 시험 문제와 연습 문제가 똑같았습니다.

11차시에서 우리는 이런 상황에 이름을 붙여 두었습니다. 본 문제를 다시 푸는 것은 실력이 아니라 과적합(overfitting)의 증거일 수도 있다고요. 14차시의 XOR은 데이터가 넷뿐이라 애초에 훈련과 테스트를 나눌 수조차 없었습니다. 그래서 우리는 아직 한 번도 ‘성능을 쟀다’고 말한 적이 없습니다.

비어 있는 고등학교 급식실. 칸막이를 세운 흰 식탁과 분홍 의자가 줄지어 있고, 앞쪽에 스테인리스 배식대가 놓여 있다
비어 있는 어느 고등학교 급식실입니다. 이 식탁들에서 날마다 잔반이 나오고, 그 양은 날씨와 메뉴에 따라 달라집니다. 2차시에는 그 양을 “오늘 몇 kg이 남을까”로 물었고, 오늘은 “오늘 많이 남을까, 아닐까”를 묻습니다 — 신경망의 출력이 0과 1 사이라서 숫자를 맞히는 문제가 아니라 둘 중 하나를 고르는 문제로 바꿔야 하기 때문입니다. 출처: Ihschooljs, Wikimedia Commons (CC BY-SA 3.0)

오늘 우리가 할 일은 세 문장입니다.

  • 14차시의 코드를 그대로 가져온다. sig·make_net·forward·train·predict 다섯 함수를 글자 하나 바꾸지 않고 맨 위에 다시 싣습니다.
  • 데이터만 갈아 끼운다. 네 점 대신 급식실 기록 240일치를 넣습니다. 11차시의 규칙대로 훈련과 테스트로 가릅니다.
  • 12차시의 네 칸으로 잰다. 정확도 하나가 아니라 정밀도와 재현율까지 함께 적습니다.
💭 오늘의 물음

코드를 하나도 안 고치고 데이터만 바꾸면, 정말로 다른 문제가 풀릴까?
그리고 그 결과가 ‘좋다’는 것을 무엇을 보고 말할 수 있을까?

답을 미리 말해 두겠습니다. 풀립니다. 테스트 정확도 80.6%가 나옵니다. 그런데 이 숫자만 보고 “잘했다”고 하면 거의 아무것도 말하지 않은 것입니다. 왜 그런지가 오늘 수업의 절반입니다.

1

코드는 그대로, 데이터만 바뀐다

오늘 쓰는 파이썬 파일의 맨 위 절반 이상은 새로 쓴 것이 아닙니다. 네 차시에서 만들어 둔 함수를 그대로 옮겨 실었을 뿐입니다.

가져온 함수어느 차시에서거기서 무엇을 했나오늘 무엇에 쓰나
sig · make_net · forward
train · predict
14차시XOR 네 점을 학습급식 168일을 학습
dist · minmax_fit
minmax_apply
5차시 → 8차시걸음 수와 공부 시간의 단위 맞추기기온과 체육 여부의 단위 맞추기
nearest · knn · accuracy 8차시 → 11차시씨앗 55알의 품종 맞히기신경망과 겨룰 상대
confusion · scores 12차시1,000명 검사 결과를 네 칸으로오늘의 성적표
네 차시에서 온 함수 열셋. 이 표가 오늘의 논지입니다 — 바뀌는 것은 데이터뿐입니다.

여기서 한 가지를 분명히 해 둡시다. 왜 함수를 다시 실어야 할까요? 브라우저 속 파이썬은 한 쪽(page)이 열려 있는 동안만 기억합니다. 차시가 바뀌면 새 쪽이 열리고, 앞 차시에서 정의한 train()은 사라집니다. 그래서 첫 줄에서 NameError가 납니다. 실제 개발에서도 마찬가지예요 — 다른 파일의 함수를 쓰려면 import로 가져와야 합니다. 우리는 import 대신 복사해서 다시 싣는 방식을 씁니다. 코드가 눈앞에 다 보이는 편이 배우기에 낫기 때문입니다.

5차시 · 8차시 dist · minmax_* 8차시 · 11차시 knn · accuracy 12차시 confusion · scores 14차시 train · predict 오늘 새로 쓴 것 lesson15.py 가져온 함수 13개 (약 100줄) 한 글자도 안 고침 데이터 · 가르기 · 평가 오늘의 성적표 훈련 168일 · 테스트 72일 80.6% 테스트 정확도 정밀도 64.9% · 재현율 96.0% 기준선은 65.3% 였다

그림 1. 왼쪽 넷은 지난 차시에서 그대로 옮겨 온 것이고, 붉은 점선 하나만 오늘 새로 씁니다. “딥러닝을 활용한다”는 말의 실제 모습이 이렇습니다 — 모델을 새로 발명하는 것이 아니라, 있는 모델에 내 데이터를 붙이는 일입니다.

⚠️ 이름이 겹치는 자리 하나

14차시에서 만든 학습 함수의 이름이 train입니다. 그런데 11차시에서는 훈련 데이터를 담는 변수를 train이라고 불렀습니다 (train, test = data[:45], data[45:]).

그 줄을 그대로 오늘 파일에 옮기면 학습 함수가 통째로 사라지고 다음 줄에서 TypeError: 'list' object is not callable이 납니다. 그래서 오늘은 train_set / test_set이라고 씁니다. 같은 이름을 다른 뜻으로 쓰면 조용히 부서진다 — 코드를 이어 붙일 때 가장 흔한 사고입니다.

2

오늘의 데이터 — 급식실 240일치

2차시에서 우리는 급식 잔반 기록을 놓고 “오늘 몇 kg이 남을까”를 물었습니다. 오늘은 같은 급식실을 놓고 다른 물음을 던집니다 — “오늘은 많이 남을까, 아닐까?”

물음을 바꾼 데에는 까닭이 있습니다. 14차시의 신경망은 출력이 0과 1 사이의 값 하나입니다. 시그모이드를 통과했으니까요. 몇 kg 이라는 숫자를 바로 뱉을 수 없습니다. 그래서 잔반량을 두 갈래로 접어서, ‘많음(1)’과 ‘적음(0)’을 맞히는 분류 문제로 만들었습니다. 문제의 모양을 내가 가진 도구에 맞추는 것도 인공지능으로 문제를 푸는 일의 한 부분입니다.

ℹ️ 2차시의 급식 데이터와 오늘의 급식 데이터는 다른 것입니다

2차시는 60일치 표(요일 · 메뉴 · 만족도까지 있는)를 썼고, 오늘은 새로 크게 만든 240일치를 씁니다. 문제는 같지만 데이터는 다릅니다 — 신경망을 훈련과 테스트로 가르려면 60행으로는 너무 적기 때문입니다. 2차시의 숫자(평균 오차 1.98kg 같은 것)를 오늘 결과와 나란히 놓으면 안 됩니다.

데이터는 이렇게 생겼습니다. 하루가 한 줄이고, 입력 특성 넷과 이름표 하나입니다.

칸무엇범위왜 이 값을 쓰나
특성 ①낮 최고 기온1.1 ~ 34.0 ℃ 더울수록 뜨거운 음식이 남는다
특성 ②메뉴의 뜨거움0 ~ 10 0 = 냉면 · 샐러드, 10 = 국 · 찌개
특성 ③메뉴 선호도1.1 ~ 5.0 전교생 설문 평균
특성 ④4교시 체육 여부0 또는 1 배가 고프면 남기지 않는다
이름표오늘 많이 남았나1 = 많음, 0 = 적음 이것을 맞히는 것이 목표다
기온과 선호도의 범위(1.1∼34.0, 1.1∼5.0)는 훈련 168일에서 실제로 뽑힌 최소 · 최대입니다.

앞의 여섯 날을 그대로 옮겨 적으면 이렇습니다. 오늘 실습에서 첫 화면에 나오는 바로 그 여섯 줄입니다.

날기온메뉴온도선호도 4교시체육잔반읽는 법
132.903.11 적음더운 날 + 찬 메뉴 → 궁합이 맞는다
28.904.50 적음추운 날 찬 메뉴인데도 선호도가 높다
334.031.50 적음선호도가 바닥인데도 남지 않았다
413.951.90 많음어중간한 기온 + 어중간한 온도 + 낮은 선호도
527.332.20 적음더운 날 시원한 편 → 맞는다
620.063.00 많음기온도 온도도 가운데 — 가장 어긋나는 자리
240일 중 ‘많이 남은 날’은 97일(40.4%)입니다.

숨은 규칙은 XOR과 같은 모양이다

이 데이터를 만든 규칙은 한 문장입니다. 날씨와 메뉴의 궁합이 어긋나면 남는다. 더운 날에 펄펄 끓는 국이 나오면 남고, 추운 날에 찬 냉면이 나와도 남습니다. 반대로 더운 날의 찬 메뉴, 추운 날의 뜨거운 국은 잘 먹습니다.

3번 날을 다시 보세요. 기온 34.0℃에 메뉴 온도 3, 선호도는 1.5로 바닥인데도 적음입니다. 기온만 봐도, 메뉴 온도만 봐도 설명이 안 됩니다. 두 값이 서로 얼마나 어긋났는가가 답을 정합니다.

더운 날 · 찬 메뉴

기온 높음 + 온도 낮음 → 잘 먹는다

추운 날 · 뜨거운 국

기온 낮음 + 온도 높음 → 잘 먹는다

더운 날 · 뜨거운 국

기온 높음 + 온도 높음 → 남는다

추운 날 · 찬 냉면

기온 낮음 + 온도 낮음 → 남는다

이 네 칸을 13차시의 XOR 진리표와 나란히 놓아 보세요. 완전히 같은 모양입니다. 둘이 같으면 0, 다르면 1 — 그것이 XOR이었지요. 그래서 13차시의 뉴런 하나로는 이 데이터도 제대로 못 나눕니다. 14차시에서 은닉층을 붙인 일이 여기서 값을 합니다.

💡 100%가 나올 수 없게 일부러 만들어 두었다

이 데이터에는 기록이 잘못 남은 날이 섞여 있습니다. 급식실 담당 선생님이 바쁜 날 숫자를 잘못 적었다고 생각하면 됩니다. 열 번에 한 번 꼴로 이름표를 뒤집어 두었고, 실제로는 240일 중 21일(8.8%)이 그렇게 되었습니다.

왜 일부러 흠집을 냈을까요? 진짜 데이터에는 반드시 이런 날이 있기 때문입니다. 흠집이 없는 데이터로만 연습하면 “정확도 100%가 목표”라는 잘못된 감각이 생깁니다. 오늘 여러분은 천장이 100%가 아닌 문제를 처음 만납니다.

3

재기 전에 해야 하는 두 가지 — 가르기와 단위 맞추기

학습 버튼을 누르기 전에 반드시 먼저 하는 일이 둘 있습니다. 11차시의 가르기와 5차시의 정규화입니다. 둘 다 지난 차시에 배운 것이지만, 오늘 처음으로 둘을 한 번에 씁니다.

① 240일을 훈련 168일과 테스트 72일로 가른다

비율은 70대 30입니다. 앞에서부터 자르지 않고 먼저 섞은 뒤 자릅니다. 섞는 데 쓴 씨앗은 150이라 몇 번을 돌려도 같은 결과가 나옵니다.

무엇훈련테스트뜻
날 수168일72일70% / 30%
‘많이 남은 날’72일25일비율이 비슷하게 갈렸다
기록이 잘못된 날17일4일 테스트 72일 중 4일은 맞힐 수 없다
테스트 정확도의 천장—94.4% 규칙을 완벽히 배워도 여기까지
68 ÷ 72 = 94.4%. 이 숫자를 미리 알아 두는 것이 중요합니다 — 80.6%가 나왔을 때 “100%까지 19.4%포인트 남았다”가 아니라 “13.8%포인트 남았다”가 옳은 읽기입니다.

왜 섞을까요? 우리 데이터는 사실 섞지 않아도 결과가 거의 같습니다 (실제로 재 보면 테스트 83.3%로 오히려 조금 높습니다). 컴퓨터가 만들어 낸 240일이라 날짜 순서에 아무 뜻이 없기 때문입니다.

그런데도 섞어야 합니다. 진짜 급식실 기록이었다면 앞 168일은 3월부터 11월까지이고 뒤 72일은 겨울이 됩니다. 그러면 모델은 겨울을 한 번도 못 보고 겨울 시험을 치릅니다. 섞는 이유는 ‘점수가 올라가서’가 아니라 ‘시험이 공정하려면’입니다. 실험 결과가 그렇게 나오지 않았을 때도 원리를 지키는 것 — 그것이 규칙을 아는 사람의 태도입니다.

② 네 특성의 단위를 0∼1로 맞춘다

기온은 1∼34이고, 4교시 체육 여부는 0 아니면 1입니다. 범위가 33배 차이입니다. 5차시에서 걸음 수(5100)와 공부 시간(4.5)을 그대로 두면 거리가 걸음 수에만 좌우되던 것, 기억하시죠? 여기서도 똑같은 일이 벌어집니다.

첫 훈련 날의 입력기온메뉴온도선호도체육
날것 그대로26.331.20
0∼1로 옮긴 뒤0.7660.3000.0260.000
기준(최소 · 최대)은 훈련 168일에서만 뽑습니다. 테스트를 함께 넣으면 시험 문제를 미리 본 셈이 됩니다.

정규화를 빼기만 하면 어떻게 될까요? 오늘 실습에서 직접 확인합니다. 결과를 미리 말하면 테스트 정확도가 47.2%로 무너집니다. 동전을 던지는 것보다 못하고, 아무 생각 없이 늘 ‘적음’이라고만 답하는 기준선(65.3%)보다도 18%포인트 낮습니다. 알고리즘은 하나도 안 건드렸는데 한 줄을 빼서 그렇게 됩니다.

⚠️ 최소 · 최대를 테스트까지 넣어 재면? — 정직하게 말하면 이번에는 손해가 없었다

데이터 누수의 대표 사례라고 배웠으니 점수가 확 부풀 것 같지만, 실제로 재 보면 손실 0.0958 → 0.0957, 정확도는 소수점까지 그대로입니다. 240일이 같은 규칙으로 만들어져 훈련 168일의 최소 · 최대가 이미 전체와 거의 같기 때문입니다 (훈련 기온 1.1∼34.0 = 전체 1.1∼34.0).

그렇다고 해도 해도 되는 일이 되지는 않습니다. 테스트에 훈련보다 훨씬 추운 날이 하나라도 있으면 이야기가 달라지고, 무엇보다 “해 봤더니 괜찮더라”는 근거로는 다음번을 보장할 수 없습니다. — 실험이 극적이지 않을 때 그것을 그대로 적는 것도 데이터를 다루는 사람의 일입니다.

4

네 칸으로 잰다 — 정확도 하나로는 모자라다

은닉 노드 3개, 학습률 0.5로 300번 돌린 결과입니다. 손실은 0.2359에서 0.0958까지 내려갔습니다.

반복150100 150200250300
훈련 손실0.23590.17260.1238 0.10720.10210.09860.0958
손실은 평균제곱오차입니다 — 9차시에서 쓰던 그 자를 그대로 씁니다.
💡 손실이 0으로 안 갑니다 — 그게 정상입니다

14차시의 XOR에서는 손실이 0.000383까지 떨어졌습니다. 오늘은 0.0958에서 거의 멈춥니다. 덜 배운 걸까요? 아닙니다. 이 데이터에는 기록이 잘못 남은 날이 10%쯤 섞여 있어서 0으로 갈 수가 없습니다. 만약 손실이 0에 가까이 간다면 그건 잡음까지 통째로 외웠다는 뜻이고, 11차시가 말한 과적합의 신호입니다. 현실 데이터에서 손실 0은 좋은 신호가 아니라 나쁜 신호입니다.

그래서, 얼마나 잘하는가

12차시에서 만든 confusion()에 (실제, 예측) 짝을 넣으면 네 칸이 나옵니다. 테스트 72일의 결과입니다.

TP — 맞게 잡음24

많이 남는다고 했고 진짜로 많았다

FP — 헛짚음13

많이 남는다고 했는데 적었다 — 밥을 괜히 덜 지었다

FN — 놓침1

적다고 했는데 진짜는 많았다 — 버려진 날

TN — 맞게 넘김34

적다고 했고 진짜 적었다

무엇훈련 168일테스트 72일어떻게 읽나
네 칸 (TP/FP/FN/TN)67 / 17 / 5 / 7924 / 13 / 1 / 34—
정확도86.9%80.6% 전체 중 맞힌 비율
정밀도79.8%64.9% ‘많음’이라 한 37일 중 진짜는 24일
재현율93.1%96.0% 진짜 많았던 25일 중 24일을 잡았다
F185.9%77.4% 정밀도와 재현율의 조화평균

이제 이 글 처음에 던진 물음으로 돌아옵시다. “테스트 정확도 80.6%”만 보고하면 무엇이 감춰질까요?

정밀도 64.9%가 감춰집니다. 모델이 “오늘 많이 남습니다”라고 말한 날은 37일인데, 그중 진짜로 많이 남은 날은 24일뿐입니다. 급식실이 이 예측을 그대로 믿고 밥을 덜 지으면 13일은 헛되이 모자랍니다. 그날 점심을 못 먹는 학생이 생긴다는 뜻이지요.

반대로 재현율은 96.0%로 아주 높습니다. 진짜 많이 남은 25일 중 24일을 잡아냈습니다. 놓친 날은 하루뿐입니다. 정리하면 이 모델은 놓치는 쪽에는 강하고, 헛짚는 쪽에 약합니다. “80.6%”라는 한 숫자에는 이 성격이 하나도 담겨 있지 않습니다.

⚠️ 짝의 순서를 뒤집으면 — 오류 없이 돌면서 답만 틀린다

12차시가 정한 confusion()의 짝은 (실제, 예측) 순서입니다. (예측, 실제)로 넣어도 오류가 나지 않습니다. 대신 FP와 FN만 자리를 바꾸어, 정밀도와 재현율이 64.9%와 96.0%에서 96.0%와 64.9%로 뒤바뀝니다. 정확도와 F1은 한 자리도 안 바뀌므로 눈치채기가 아주 어렵습니다.
검산하는 법: 재현율의 분모 TP + FN이 실제 양성 날 수와 같은지 봅니다 (24 + 1 = 25 ✔).

기준선과 나란히 놓아야 뜻이 생긴다

80.6%는 높은 걸까요, 낮은 걸까요? 혼자서는 대답할 수 없습니다. 아무것도 학습하지 않은 모델과 견줘야 합니다. 훈련 데이터에서 더 흔한 쪽은 ‘적음’이므로, 기준선은 “무슨 일이 있어도 늘 ‘적음’이라고 답하는 모델”입니다.

모델훈련테스트
기준선 — 늘 ‘적음’57.1%65.3%
우리 신경망 (은닉 3 · lr 0.5 · 300회)86.9%80.6%
차이+29.8%p+15.3%p
테스트 정확도의 천장이 94.4%임을 떠올리면, 기준선 65.3%에서 천장까지 29.1%포인트 중 15.3%포인트를 갔습니다.

이 +15.3%포인트가 인공지능이 실제로 한 일입니다. “80.6% 나왔어요”가 아니라 “기준선 65.3%에 견줘 15.3%포인트 올렸고, 천장은 94.4%입니다” — 이렇게 말할 수 있어야 성능을 평가했다고 할 수 있습니다.

💻

손으로 ① — 학습 관제판

이제 여러분이 직접 돌릴 차례입니다. 아래 관제판은 파이썬을 부르지 않고 브라우저가 그 자리에서 계산합니다. 방금 본 값(은닉 3 · lr 0.5 · 300회)이 기본값이니, 먼저 [▶ 학습]을 그대로 눌러 위 표의 숫자가 그대로 나오는지 확인하세요.

🎛️ 학습 관제판 — 두 손실 곡선과 네 칸 INTERACTIVE

은닉 노드 수 · 학습률 · 반복 수 · 씨앗 둘을 고르고 [▶ 학습]을 누르면, 훈련 손실과 테스트 손실 두 곡선이 한 걸음씩 함께 그려집니다. 두 곡선이 갈라지기 시작하는 자리에 세로선이 그어지고, 학습이 끝나면 12차시의 네 칸이 채워집니다.

훈련 손실 테스트 손실 테스트 손실이 바닥을 친 자리 직전 결과
훈련 손실—
테스트 손실—
훈련 정확도—
테스트 정확도—
기준선 대비—
테스트 손실 바닥—
훈련 168일의 네 칸
TP 맞게 잡음—
FP 헛짚음—
FN 놓침—
TN 맞게 넘김—

정밀도 — · 재현율 — · F1 —

테스트 72일의 네 칸 — 이쪽이 진짜 성적표
TP 맞게 잡음—
FP 헛짚음—
FN 놓침—
TN 맞게 넘김—

정밀도 — · 재현율 — · F1 —

[안내] [▶ 학습]을 누르면 두 곡선이 그려집니다. 기본값은 은닉 3개 · 학습률 0.5 · 300회입니다.

과제 ① — 표를 채운다

아래 세 설정을 차례로 돌려 공책에 옮겨 적으세요. 칸에 바로 적어도 됩니다. 격차는 훈련 정확도에서 테스트 정확도를 뺀 값입니다. 마지막 칸 ‘테스트 손실 바닥’은 관제판이 알려 주는 회차이고, “(끝)”이라고 나오면 아직 바닥에 닿지 않았다는 뜻입니다.

은닉학습률반복훈련 정확도테스트 정확도격차(%p)테스트 손실 바닥
30.5300
30.51200
20.5150

과제 ② — 곡선이 돌아서는 회차를 찾는다

반복 수를 1200으로 놓고 [▶ 학습]을 누르세요. 훈련 손실(하늘색)은 끝까지 내려가는데, 테스트 손실(주황색)은 어느 지점에서 바닥을 치고 다시 올라옵니다. 관제판이 그 자리에 붉은 세로선을 긋고 회차를 알려 줍니다. 그 회차를 적어 두세요. 이어서 은닉을 2개 · 3개 · 5개로 바꿔 가며 그 회차가 어디로 옮겨 가는지 보세요 — 5절의 마지막 표와 맞아떨어져야 합니다.

반복 수를 300쯤으로 짧게 잡으면 바닥이 다른 회차로 찍히거나 “아직 내려가는 중”이라고 나옵니다. 아직 바닥에 닿지 않았거나, 거의 평평한 구간에서 아주 작은 굴곡을 바닥으로 집었기 때문입니다. (은닉 3개 · 300회에서는 235회가 찍히는데, 1200회까지 보면 진짜 바닥은 386회입니다.) 곡선이 평평할 때 최저점 한 점을 콕 집는 일은 원래 이렇게 위태롭습니다 — 6절에서 다룰 이야기가 여기서도 한 번 나옵니다.

과제 ③ — 반례를 만든다

“훈련 정확도는 오르는데 테스트 정확도는 오히려 떨어지는” 설정을 하나 만들어 보세요. 그리고 분할 씨앗만 150에서 1로 바꿔 다시 돌려 보세요. 다른 것은 하나도 안 건드렸는데 테스트 정확도가 얼마나 움직이나요?

💡 관제판이 파이썬과 같은 값을 내는지 의심해 보세요

이 관제판은 파이썬의 난수 발생기까지 그대로 옮겨 만든 것이라, 아래 파이썬 실습과 소수점 아래까지 같은 값이 나오도록 되어 있습니다. 두 곳의 숫자를 나란히 놓고 대조해 보세요 — 다르면 둘 중 하나가 틀린 것입니다. 다만 학습률을 5 이상으로 올리면 이야기가 달라집니다. 그 까닭은 조금 뒤 6절에서 다룹니다.

💻

손으로 ② — 파이썬으로 직접 학습시키고 평가하기

아래 코드에는 빈칸이 다섯 곳 있습니다. ?????를 지우고 식을 채우면 돌아갑니다. 맨 위 ‘가져온 부분’ 세 덩어리에는 빈칸이 없습니다 — 이미 앞 차시에서 채운 것을 다시 채우게 하면 “바뀌는 것은 데이터뿐”이라는 오늘의 논지가 무너지기 때문입니다. 빈칸은 전부 데이터를 다루는 자리에 있습니다.

빈칸무엇을 채우나틀리면 어떻게 되나
①자르는 지점 (70%)소수가 되어 슬라이스에서 TypeError
②최소 · 최대를 뽑을 데이터 오류 없이 돈다. 테스트를 미리 본 데이터 누수가 된다
③confusion()에 넣을 짝의 순서 오류 없이 돈다. 정밀도와 재현율만 서로 바뀐다
④정확도 계산== t를 빠뜨리면 맞힌 날이 아니라 ‘많음’이라 답한 날을 세어 51.4%가 나온다
⑤기준선이 고를 답0과 1을 바꾸면 65.3%가 34.7%로 뒤집힌다
②③는 돌아가는데 답이 틀리는 종류입니다. 오류 메시지가 알려 주지 않으니 원장의 숫자와 대조해야만 잡힙니다 — 정밀도 64.9% · 재현율 96.0%가 나와야 맞습니다.

실행이 되면 4절의 표와 한 자리도 다르지 않은 숫자가 나와야 합니다. 네 칸이 24 / 13 / 1 / 34인지, 기준선이 65.3%인지 확인하세요.

ℹ️ 다음 두 칸은 위 칸을 먼저 실행한 뒤에 실행합니다

같은 쪽 안에서는 파이썬이 기억을 이어 갑니다. 위 칸을 실행했다면 net·Xte·knn이 아래 칸에서도 그대로 살아 있습니다. 먼저 실행하지 않고 아래를 누르면 NameError: name 'confusion' is not defined처럼 이름을 못 찾겠다는 오류가 납니다 — 어느 이름이 뜨는지는 그 칸이 먼저 읽는 이름에 달렸습니다 (②-3 칸은 train_set이라고 나옵니다). 1절에서 말한 ‘차시가 바뀌면 잊는다’가 바로 이것입니다 — 쪽이 바뀌면 위 칸의 기억도 사라집니다.

📝 여기까지 하고 공책에 적을 것
  • 테스트 네 칸 네 숫자와 정밀도 · 재현율
  • k-NN이 가장 잘한 k와 그때의 테스트 정확도
  • 정규화를 뺐을 때의 테스트 정확도 — 기준선보다 높은가, 낮은가
5

표가 말한 것 — 은닉을 늘리면 똑똑해지는가

관제판으로 은닉 노드 수와 학습률을 짝지어 열두 판을 돌리면 아래 표가 나옵니다 (반복 150회 · 학습 씨앗 1로 고정). 칸은 훈련% / 테스트% (격차)입니다.

은닉 \ 학습률0.050.52.0
2개76.2 / 68.1 (+8.1) 84.5 / 86.1 (−1.6)82.7 / 80.6 (+2.2)
3개76.2 / 68.1 (+8.1) 85.7 / 83.3 (+2.4)83.9 / 83.3 (+0.6)
5개76.2 / 68.1 (+8.1) 85.1 / 83.3 (+1.8)84.5 / 77.8 (+6.7)
8개76.2 / 68.1 (+8.1) 85.1 / 83.3 (+1.8)87.5 / 83.3 (+4.2) *
* 이 한 칸만 관제판이 86.9 / 83.3을 낼 수 있습니다. 까닭은 다음 절에서 다룹니다.

이 표에서 가장 먼저 눈에 띄는 것은 왼쪽 줄이 통째로 똑같다는 사실입니다. 학습률 0.05에서는 은닉을 2개로 하든 8개로 하든 76.2 / 68.1로 한 자리도 안 변합니다. 은닉 노드를 네 배로 늘렸는데 결과가 완전히 같다니, 이상하지 않나요?

까닭은 간단합니다. 학습률 0.05는 걸음이 너무 작아서, 150번을 걸어도 출발선에서 거의 못 벗어납니다. 은닉 노드가 몇 개든 아직 아무것도 안 배운 상태예요. 그러니 이 표의 결론은 “은닉을 늘리면 좋아진다”가 아니라 “학습률이 맞아야 은닉이 비로소 일을 한다”입니다.

둘째로 눈여겨볼 것은 테스트가 가장 높은 칸이 은닉 2개라는 점입니다(86.1%). 우리가 4절에서 고른 은닉 3개는 같은 조건(150회)에서 83.3%이니, 은닉 2개가 그보다 높습니다. (4절의 80.6%는 300회까지 돌린 값이라 이 표와 바로 견주면 안 됩니다.) 은닉을 8개까지 늘려도 테스트는 83.3%에서 더 오르지 않습니다. 처음 고른 설정이 최선이 아니었다는 것 — 그것이 이 표를 채우는 이유입니다.

⚠️ 86.1%가 83.3%보다 ‘낫다’고 말하면 안 된다

테스트 데이터가 72일뿐입니다. 한 날을 더 맞히면 정확도가 1.4%포인트 움직입니다. 86.1%와 83.3%의 차이는 딱 두 날이에요. 운이 조금만 달랐어도 뒤집힐 차이입니다.
3%포인트 안쪽의 차이로는 우열을 가릴 수 없습니다. 시험 성적을 한 문제 차이로 등수 매기지 않는 것과 같은 이치입니다.

반복 수를 늘리면 — 두 곡선이 갈라진다

이번에는 은닉 5개 · 학습률 0.5로 고정하고 반복 수만 늘려 봅시다.

반복훈련 손실훈련 정확도 테스트 정확도격차
100회0.114184.5%83.3%+1.2
400회0.077891.1%84.7%+6.3
1200회0.064791.7%83.3%+8.3
훈련 손실은 0.1141 → 0.0647로 계속 내려갑니다. 그런데 테스트 정확도는 400회에서 정점을 찍고 내려옵니다.

이것이 11차시의 과적합 곡선이 신경망에서 그대로 재현된 자리입니다. 더 오래 공부시켰더니 훈련은 잘하는데 시험은 더 못 봅니다. 관제판에서 테스트 손실 곡선을 보면 이 순간이 눈으로 보입니다.

반복1100200 3004006008001200
훈련 손실0.23590.12380.1021 0.09580.09220.08930.08800.0846
테스트 손실0.24370.13450.1195 0.11900.11850.12120.12460.1447
은닉 3개 · 학습률 0.5. 테스트 손실의 바닥은 386회(0.1185)이고, 거기서부터 두 곡선이 갈라집니다.

주목할 것은 훈련 손실은 끝까지 한 번도 안 올라간다는 점입니다. 1200회에서 0.0846으로 여전히 최솟값이에요. 훈련 손실만 보고 있으면 이 사고를 절대 알아챌 수 없습니다. 테스트 손실을 함께 그려야만 “여기서 멈췄어야 했다”가 보입니다.

바닥의 자리는 은닉 노드 수에 따라 달라집니다. 관제판으로 직접 확인해 보세요.

은닉테스트 손실 바닥1200회의 훈련 / 테스트 손실 1200회의 훈련 / 테스트 정확도
2개183회 (0.1063)0.0928 / 0.109589.9% / 86.1%
3개386회 (0.1185)0.0846 / 0.144787.5% / 80.6%
5개491회 (0.1006)0.0647 / 0.132691.7% / 83.3%
은닉이 클수록 바닥이 늦게 오고, 그 뒤로 더 가파르게 벌어집니다 — 큰 모델일수록 오래 돌리면 더 크게 망가진다는 뜻입니다.

한 번의 가르기에 얼마나 휘둘리는가

마지막으로 분할 씨앗만 바꿔 보겠습니다. 데이터도, 모델도, 설정도 전부 그대로입니다. 240일을 어떤 순서로 섞느냐만 다릅니다.

데이터를 어떻게 가르느냐

분할 씨앗 150 → 테스트 80.6%
씨앗 1 → 61.1% · 씨앗 2 → 70.8%
씨앗 3 → 81.9% · 씨앗 7 → 75.0%
폭 20.8%포인트

어디서 시작하느냐

학습 씨앗 1 → 테스트 80.6%
씨앗 2 → 81.9% · 씨앗 3 → 84.7%
씨앗 4 → 83.3% · 씨앗 5 → 84.7%
폭 4.1%포인트

데이터를 어떻게 가르느냐가, 어디서 시작하느냐보다 5배나 크게 영향을 미칩니다. 한 번 가르고 한 번 재서 나온 “80.6%”는 그 한 번의 운까지 함께 담고 있는 숫자입니다. 11차시에서 “한 번의 운 나쁜 분할”을 이야기했지요. 이것이 그 실측입니다. 실제 연구에서는 그래서 여러 번 다르게 갈라 평균을 내는 방법(교차 검증)을 씁니다.

한편 14차시에서는 시작 가중치가 성패를 갈랐습니다 — 다섯 번 중 한 번은 아예 학습에 실패했지요. 그런데 오늘은 어느 씨앗으로 시작해도 4.1%포인트 안에 들어옵니다. 데이터가 네 점에서 168일로 늘어나자 운의 몫이 줄어든 것입니다.

💬 조별로 나눠 해 보기

모둠마다 다른 분할 씨앗(1 · 2 · 3 · 7 · 150 · 그 밖의 아무 수)을 정해 은닉 3개 · 0.5 · 300회로 돌리고, 테스트 정확도를 칠판에 모아 보세요. 숫자들이 얼마나 흩어지는지를 보면 “정확도 하나를 보고한다”는 일이 얼마나 위태로운지 느껴집니다.

6

학습률을 너무 크게 하면 — 그리고 마지막 자리가 흔들릴 때

9차시에서 경사하강법의 학습률을 키웠을 때, 손실이 폭발하며 숫자가 발산했던 것을 기억하시나요? 오늘도 같은 일이 날까요? 은닉 3개 · 150회로 고정하고 학습률만 올려 보겠습니다.

학습률손실훈련테스트 테스트 앞 네 날의 출력
0.50.107285.7%83.3% [0.983, 0.36, 0.609, 0.742]
5.00.143181.0%79.2% [1.0, 0.877, 0.999, 0.503]
20.00.217175.0%66.7% [0.937, 0.0, 0.937, 0.933]
100.00.571442.9%34.7% [1.0, 1.0, 1.0, 1.0]

답은 아닙니다. 오류도 나지 않고 숫자가 무한대로 튀지도 않습니다. 대신 학습률 100에서는 무엇을 물어도 1.0이라고만 답하는 모델이 되었습니다. 테스트 정확도 34.7%는 “늘 많음이라고만 답할 때의 정확도”와 정확히 같은 값입니다. 아무것도 배우지 않은 채, 한쪽으로만 답하는 기계가 된 것이지요.

9차시와 왜 다를까요? 시그모이드가 출력을 0과 1 사이로 눌러 주기 때문입니다. 아무리 큰 값이 들어와도 결과는 1을 넘지 못합니다. 그래서 여기서는 폭발 대신 포화(saturation)가 일어납니다. 값이 양 끝에 딱 붙어 버려서, 기울기가 0이 되고 더 이상 어느 쪽으로도 움직일 수 없게 됩니다. 겉으로는 조용히 돌아가는데 속으로는 완전히 굳은 것입니다 — 오류 메시지가 뜨는 것보다 오히려 더 위험한 고장입니다.

앞 절에서 별표를 붙인 그 한 칸

5절 표에서 ‘은닉 8개 · 학습률 2.0’ 칸에만 별표를 달아 두었습니다. 파이썬은 87.5 / 83.3을 내고, 관제판은 86.9 / 83.3을 낼 수 있습니다. 왜 그럴까요?

먼저 의심할 것은 “둘 중 하나가 잘못 짜였다”는 가능성입니다. 그래서 실험을 하나 했습니다. 파이썬 안에서, 입력값 딱 하나를 표현할 수 있는 가장 작은 단위만큼 흔들어 보았습니다. 소수점 열여섯째 자리쯤에서 1이 바뀌는 정도, 사실상 “보이지도 않는” 변화입니다.

설정원래 값한 자리만 흔든 뒤
은닉 1 · lr 0.5 · 300회76.2 / 68.1똑같다
은닉 3 · lr 0.5 · 300회86.9 / 80.6똑같다
은닉 3 · lr 2.0 · 150회83.9 / 83.3똑같다
은닉 8 · lr 2.0 · 150회87.5 / 83.386.9 / 83.3
은닉 3 · lr 5.0 · 150회81.0 / 79.281.0 / 86.1
은닉 3 · lr 20.0 · 150회75.0 / 66.767.3 / 68.1
은닉 3 · lr 100.0 · 150회42.9 / 34.7똑같다
왼쪽 값은 전부 원장에서 실제로 돌려 얻은 것입니다. 오른쪽은 입력 하나를 1 단위만 흔든 결과입니다.

결과가 분명합니다. 학습률이 작을 때는 아무리 흔들어도 결과가 꿈쩍하지 않습니다. 그런데 학습률이 5를 넘어가면 보이지도 않는 한 자리 차이가 6.9%포인트를 움직입니다. 관제판과 파이썬이 다른 값을 내는 것은 어느 한쪽이 틀려서가 아니라, 그 구간에서는 ‘정답’이라 부를 만한 값이 애초에 없기 때문입니다. (컴퓨터마다 지수함수를 마지막 자리에서 조금씩 다르게 계산하는데, 평소에는 아무 문제가 없다가 이 구간에서만 결과를 갈라 놓습니다.)

학습률 100에서는 다시 똑같아지는 것도 재미있습니다. 이미 완전히 포화되어 굳었기 때문에 흔들 여지가 없는 것이지요.

ℹ️ 이것을 재현성(reproducibility) 문제라고 부릅니다

인공지능 논문에서 “정확도 87.5%”라고 쓰인 숫자가 다른 사람의 컴퓨터에서는 86.9%로 나오는 일이 실제로 일어납니다. 그래서 요즘 연구는 씨앗을 여러 개 써서 평균과 흩어짐을 함께 보고합니다. “87.5%” 대신 “다섯 번 돌려 평균 83.0%, 폭 4.1%포인트”라고 적는 것이지요 (5절의 학습 씨앗 다섯 판이 실제로 그렇습니다).

오늘 여러분이 배울 것은 이것입니다 — 결과가 손톱만 한 흔들림에도 달라지는 구간에서는, 그 결과를 근거로 무엇을 주장하면 안 됩니다. 학습률 0.5 구간의 80.6%는 믿어도 되고, 학습률 20 구간의 66.7%는 믿으면 안 됩니다.

7

0.5라는 문턱은 어디서 왔나 — 어느 쪽 실수가 더 아픈가

14차시에서 만든 predict()는 이렇게 생겼습니다.

return 1 if o >= 0.5 else 0

출력이 0.5 이상이면 ‘많음’이라고 답합니다. 그런데 왜 하필 0.5일까요? 12차시에서 병원 검사 점수를 놓고 기준점을 이리저리 옮겨 보았던 것, 기억나시죠. 똑같은 일을 급식실에서 다시 해 봅시다. 테스트 72일에 대해 문턱만 옮긴 결과입니다.

문턱TPFPFNTN 정확도정밀도재현율
0.22419128 72.2%55.8%96.0%
0.32417130 75.0%58.5%96.0%
0.42414133 79.2%63.2%96.0%
0.52413134 80.6%64.9%96.0%
0.6228339 84.7%73.3%88.0%
0.7215442 87.5%80.8%84.0%
0.8192645 88.9%90.5%76.0%
모델은 하나도 안 바뀌었습니다. 학습도 다시 하지 않았습니다. 답을 읽는 기준만 옮겼습니다.

표가 말하는 것은 분명합니다. 0.5는 기본값일 뿐, 최선이 아닙니다. 0.8로 올리면 정확도가 80.6%에서 88.9%로 오르고, 헛짚는 날(FP)이 13일에서 2일로 줄어듭니다. 대신 놓치는 날(FN)이 1일에서 6일로 늘어납니다.

그러면 무조건 0.8이 답일까요? 답은 데이터가 아니라 급식실이 정합니다.

FP가 더 아픈 경우

“많이 남는다”는 예측을 믿고 밥을 덜 지었는데 실제로는 다 먹었다.
점심을 못 먹는 학생이 생긴다. → 문턱을 올린다

FN이 더 아픈 경우

“적게 남는다”고 보고 평소대로 지었는데 잔뜩 남았다.
음식이 버려지고 처리 비용이 든다. → 문턱을 내린다

12차시에서 “암을 놓치는 것과 괜히 재검사를 받게 하는 것 중 어느 쪽이 더 아픈가”를 물었지요. 급식실에서는 학생이 점심을 못 먹는 쪽이 대개 더 아픕니다. 그렇다면 문턱은 0.5보다 높아야 합니다. 이 판단은 코드가 아니라 사람이 합니다. — 인공지능을 ‘활용한다’는 말에는 이 판단까지 들어 있습니다.

같은 문제를 8차시의 k-NN으로 풀면

신경망이 이 문제에 정말 필요했을까요? 8차시에서 만든 k-NN을 그대로 불러 겨뤄 봅시다. 여기서도 고친 것은 데이터뿐입니다.

k135 91525
훈련100.0%91.1%88.1% 82.7%79.2%75.6%
테스트69.4%76.4%77.8% 77.8%79.2%73.6%
격차+30.6+14.7+10.3 +5.0+0.0+2.0
k = 1의 훈련 100% / 테스트 69.4% — 11차시에서 본 과적합의 그림이 그대로 다시 나옵니다.

k = 1은 훈련 데이터를 통째로 외웁니다. 자기 자신이 가장 가까운 이웃이니까요. 그래서 훈련 100%인데 테스트는 69.4%로 기준선(65.3%)보다 겨우 4.1%포인트 높습니다. 격차 +30.6%포인트 — 오늘 본 것 중 가장 큰 격차입니다. 과적합이 무엇인지 한 숫자로 보여 주는 자리예요.

방법테스트 정확도한마디
기준선 — 늘 ‘적음’65.3%아무것도 안 배웠다
신경망 은닉 1개 (13차시의 뉴런 하나)68.1%기준선보다 2.8%포인트 — 직선 하나로는 여기까지
k-NN (k = 15)79.2%그때의 네 칸 20 / 10 / 5 / 37
신경망 은닉 3개 · 300회80.6%오늘의 기본 설정
신경망 은닉 2개 · lr 0.5 · 150회86.1%표에서 가장 좋았던 칸
(테스트 정확도의 천장)94.4%잘못 기록된 4일 때문에

신경망이 k-NN을 이깁니다. 다만 1.4∼6.9%포인트 차이이고, 테스트 72일에서 1.4%포인트는 단 한 날입니다. “신경망이 k-NN보다 낫다”고 단정하기에는 근거가 얇습니다.

다만 은닉 1개(68.1%)와 은닉 3개(80.6%)의 차이는 12.5%포인트로 뚜렷합니다. 이것은 말할 수 있는 차이입니다. 급식 데이터에 XOR과 같은 ‘어긋남’이 들어 있고, 직선 하나로는 그것을 나눌 수 없다는 13·14차시의 이야기가 실생활 데이터에서 처음으로 값을 치른 자리입니다.

8

정직하게 — 내가 만든 것과 실제로 쓰이는 것 사이의 거리

오늘 여러분은 신경망을 학습시키고 성능을 쟀습니다. 그렇다면 “이제 딥러닝을 만들 줄 안다”고 말해도 될까요? 안 됩니다. 정확히 말하면 이렇습니다 — 원리는 같고 규모가 다릅니다. 그 거리를 숫자로 재 봅시다.

무엇오늘 우리가 만든 것실제로 쓰이는 것배수
학습 데이터168일치수십억∼수조 개의 예시천만 배 이상
층 수2층 (은닉 1층)수십∼수백 층수십 배
가중치 수19개수십억∼수조 개억 배 이상
학습 시간1초 남짓 (노트북)수천 대의 GPU로 수 주—
가중치 19개 = 입력 4 × 은닉 3 (12개) + 은닉 편향 3 + 출력 가중치 3 + 출력 편향 1. 연필로 종이에 다 적을 수 있는 개수입니다.

가중치 19개. 우리가 만든 “인공지능”의 전부가 숫자 열아홉 개입니다. 학습이란 그 열아홉 개를 조금씩 고치는 일이었습니다. 훈련 168일을 300번 되풀이해 훑었고, 하루에 한 번씩 고쳤으니 모두 5만 400번을 고쳐서 80.6%까지 간 셈입니다.

초록색 기판 위에 금속 덮개를 씌운 칩 네 개가 있고, 칩마다 색색의 냉각수 관이 이어진 구글 TPU v4 기판
구글이 신경망 계산만을 위해 만든 칩 TPU v4의 기판입니다. 칩 네 개가 한 장에 올라가고, 열이 많이 나서 물로 식힙니다(색색의 관). 오늘 우리가 노트북에서 1초 남짓 한 곱셈과 덧셈을 이런 칩 수천 개가 나눠 맡는 것이 대형 모델의 학습입니다. 출처: Norman P. Jouppi, George Kurian, Sheng Li, Peter Ma, Rahul Nagarajan, Lifeng Nai, Nishant Patil, Suvinay Subramanian, Andy Swing, Brian Towles, Cliff Young, Xiang Zhou, Zongwei Zhou, and David Patterson, Wikimedia Commons (CC BY 4.0)
흰 바닥의 넓은 방에 'summit' 글자와 IBM 표시가 붙은 검은 캐비닛이 끝이 안 보이게 줄지어 선 모습
미국 오크리지 국립연구소의 슈퍼컴퓨터 서밋(Summit, 2018년 공개). 계산의 대부분을 GPU가 맡는 기계로, 캐비닛이 끝이 안 보이게 늘어서 있습니다. 규모는 이만큼 달라도 오늘 우리가 쓴 train()과 고리의 모양은 똑같습니다 — 예측하고, 오차를 재고, 책임만큼 고친다. 출처: OLCF at ORNL, Wikimedia Commons (CC BY 2.0)

그러면 어디에 쓰이고 있나

오늘 우리가 한 일의 모양 — 여러 개의 특성을 넣어 ‘둘 중 하나’를 맞히고, 훈련과 테스트로 갈라 네 칸으로 재는 것 — 은 실제 현장에서 가장 흔한 인공지능의 모습입니다. 그리고 어느 분야든 정확도 하나가 아니라 어느 쪽 실수가 더 아픈지를 함께 따집니다.

쓰이는 곳무엇을 넣어 무엇을 맞히나어느 쪽 실수가 더 아픈가
의료 영상 판독사진 → 병변이 있나 없나 놓치는 쪽(FN) — 재현율을 먼저 본다
스팸 분류 (1차시)제목 · 본문 → 스팸인가 헛짚는 쪽(FP) — 중요한 메일이 사라진다
설비 고장 예측진동 · 온도 · 전류 → 곧 고장 나나 놓치면 생산이 멈춘다 — 재현율
신용 심사거래 기록 → 갚지 못할 위험이 있나 헛짚으면 사람이 부당하게 거절당한다
급식 수요 예측기온 · 메뉴 · 시간표 → 많이 남나 오늘 우리가 따진 그 문제
마지막 줄은 실제로 여러 지방자치단체가 시도하고 있는 일입니다. 잔반을 줄이면 음식물 쓰레기 처리 비용과 탄소 배출이 함께 줄어듭니다.

표의 넷째 줄을 다시 보세요. 신용 심사에서 FP는 사람 한 명이 대출을 거절당하는 일입니다. 우리 급식실에서 FP 13일은 “밥이 조금 모자란 날”이었지만, 같은 구조의 모델이 다른 곳에 놓이면 그 13이 사람의 삶이 됩니다. 3단원에서 다룰 이야기가 여기서 시작됩니다.

⚠️ 오늘 우리가 말할 수 있는 것과 없는 것

말할 수 있는 것 — “급식 잔반 240일 데이터에서, 은닉 3개짜리 신경망이 테스트 72일에 대해 정확도 80.6% · 정밀도 64.9% · 재현율 96.0%를 냈다. 기준선은 65.3%였고 천장은 94.4%였다.”

말할 수 없는 것 — “우리 학교 급식실에 이 모델을 붙이면 잔반이 줄어든다.” 우리 데이터는 컴퓨터가 만들어 낸 연습용이고, 실제 급식실의 기록이 아닙니다. 진짜로 쓰려면 진짜 기록을 모아 처음부터 다시 해야 합니다 — 2차시부터요.

📖

정리 — 오늘 한 일과 오늘 배운 태도

오늘은 단원의 산출물을 만든 날입니다. 열네 차시 동안 하나씩 만들어 온 부품이 한 파일에 모여, 처음으로 ‘실생활 문제를 풀고 성능을 평가하는’ 일이 끝까지 이어졌습니다.

  • 딥러닝을 활용한다는 것은 모델을 새로 발명하는 일이 아니다. 14차시의 train()을 한 글자도 안 고치고, 데이터만 갈아 끼워 다른 문제를 풀었습니다. 바뀐 것은 X와 y뿐입니다.
  • 훈련 정확도만 보고하면 아무것도 보고하지 않은 것이다. 훈련 86.9% / 테스트 80.6% — 둘을 나란히 적어야 합니다.
  • 정확도 하나로는 모자란다. 80.6% 뒤에는 정밀도 64.9%(헛짚은 13일)와 재현율 96.0%(놓친 1일)가 숨어 있었습니다. 네 칸을 다 적어야 모델의 성격이 드러납니다.
  • 기준선과 천장을 함께 말해야 뜻이 생긴다. 기준선 65.3% · 우리 모델 80.6% · 천장 94.4%.
  • 더 크게, 더 오래가 답이 아니다. 은닉을 8개까지 늘려도 테스트는 안 올랐고, 1200회까지 돌리자 훈련만 올라가고 테스트는 내려왔습니다. 두 손실 곡선이 갈라지는 자리(386회)가 ‘여기서 멈췄어야 했다’는 표시입니다.
  • 믿을 수 있는 숫자와 없는 숫자를 가려야 한다. 테스트 72일에서 한 날은 1.4%포인트이고, 학습률 5 이상 구간의 결과는 보이지도 않는 반올림 차이에 6.9%포인트가 흔들립니다.
ℹ️ 다음 두 차시에서는 ‘학습’이 나오지 않습니다

16차시(합성곱)는 필터의 숫자를 사람이 직접 정합니다 — 학습이 없습니다. 17차시(바이그램)는 낱말 뒤에 무엇이 오는지를 세어서 나누기만 합니다 — 신경망도 경사하강법도 없습니다. 학습이라는 일을 끝까지 해 보는 차시는 오늘이 마지막입니다. 두 차시를 배울 때 “여기에는 학습이 없다”는 것을 잊지 마세요.

✅

확인 문제

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

1. 오늘 쓴 파이썬 파일에서 14차시의 코드 중 실제로 바꾼 곳은 어디인가요? 그리고 바꾸지 않아도 됐던 부분은 무엇이었는지, 왜 바꿀 필요가 없었는지 함께 쓰세요.
📖 모범 답안

바꾼 곳: 한 군데도 없습니다. sig·make_net·forward·train·predict 다섯 함수는 글자 하나 바뀌지 않았습니다. 바꾼 것은 train()에 넘겨주는 것뿐입니다 — train(XOR_X, XOR_y, hidden=2, ...)가 train(Xtr, ytr, hidden=3, ...)가 되었을 뿐이지요.

왜 바꿀 필요가 없었나: train()이 입력 개수를 len(X[0])으로, 데이터 개수를 len(X)로 그때그때 세도록 짜여 있기 때문입니다. 입력이 2개(XOR)든 4개(급식)든, 데이터가 4행이든 168행이든 같은 코드가 돌아갑니다. 함수를 데이터에서 떼어 놓았기 때문에 이런 일이 가능합니다 — 14차시에서 통짜 스크립트가 아니라 함수로 만든 이유가 여기 있습니다.

2. (오늘 얻은 결과) 오늘 학습시킨 신경망의 테스트 네 칸 네 숫자를 적으세요. 그리고 “테스트 정확도 80.6%”라고만 보고했을 때 감춰지는 것이 무엇인지, 급식실에서 어떤 일이 벌어지는지로 설명하세요.
📖 모범 답안

네 칸: TP 24 · FP 13 · FN 1 · TN 34 (테스트 72일). 정확도 80.6% · 정밀도 64.9% · 재현율 96.0% · F1 77.4%.

감춰지는 것은 정밀도 64.9%입니다. 모델이 “오늘 많이 남습니다”라고 말한 날이 37일인데 그중 진짜는 24일뿐입니다. 급식실이 이 예측대로 밥을 덜 지으면 13일은 밥이 헛되이 모자랍니다. “80.6%”라는 숫자에는 이 13일이 전혀 담겨 있지 않습니다.

반대쪽도 함께 봐야 합니다. 재현율은 96.0%로, 진짜 많이 남은 25일 중 24일을 잡아냈습니다. 정리하면 이 모델은 놓치는 쪽에 강하고 헛짚는 쪽에 약합니다. 정확도 하나는 이 성격을 지워 버립니다.

3. (오늘 돌린 결과) 관제판에서 학습률 0.5 · 150회로 두고 은닉 노드를 2 → 3 → 5 → 8로 늘리며 훈련 정확도와 테스트 정확도를 표로 적으세요. 그리고 “은닉을 늘리면 똑똑해진다”가 맞는지 자기 표를 근거로 답하세요.
📖 모범 답안

학습률 0.5 · 150회에서: 은닉 2개 84.5 / 86.1 · 3개 85.7 / 83.3 · 5개 85.1 / 83.3 · 8개 85.1 / 83.3 (훈련 / 테스트).

맞지 않습니다. 테스트 정확도가 가장 높은 것은 가장 작은 은닉 2개였고, 3개부터 8개까지는 83.3%에서 전혀 오르지 않았습니다. 노드를 네 배로 늘려도 아무 소득이 없었습니다.

다만 두 가지를 함께 말해야 합니다. 첫째, 86.1%와 83.3%의 차이는 테스트 72일 기준 딱 두 날이라 우열을 단정할 수 없습니다. 둘째, 학습률을 0.05로 낮추면 은닉이 몇 개든 전부 76.2 / 68.1로 완전히 같아집니다 — 걸음이 너무 작아 출발선을 못 벗어나기 때문입니다. 그러니 정확한 결론은 “학습률이 맞아야 은닉이 비로소 일을 한다”입니다.

4. (오늘 돌린 결과) 관제판에서 학습률을 5 → 20 → 100으로 올려 보고 무슨 일이 났는지 관찰 결과를 쓰세요. 9차시에서 경사하강법의 학습률을 키웠을 때와 무엇이 다른가요?
📖 모범 답안

발산하지 않고 오류도 안 납니다. 학습률 100에서 손실 0.5714, 훈련 42.9% · 테스트 34.7%가 되고, 테스트 앞 네 날의 출력이 [1.0, 1.0, 1.0, 1.0]이 됩니다. 무엇을 물어도 ‘많이 남는다’고만 답하는 기계가 된 것입니다. 34.7%는 ‘늘 많음’이라고만 답할 때의 정확도와 정확히 같습니다.

9차시와 다른 점: 9차시에서는 손실이 무한대로 폭발했습니다. 오늘은 시그모이드가 출력을 0과 1 사이로 눌러 주기 때문에 폭발 대신 포화가 일어납니다. 값이 양 끝에 붙어 기울기가 0이 되고, 더는 아무 방향으로도 움직일 수 없게 굳습니다. 오류가 안 나는 만큼 알아채기가 더 어려운 고장입니다.

덧붙여, 학습률 5∼20 구간의 숫자는 믿으면 안 됩니다. 입력값을 소수점 열여섯째 자리에서 1만큼 흔들기만 해도 테스트 정확도가 79.2%에서 86.1%로 뜁니다. 그래서 관제판과 파이썬의 값이 이 구간에서 서로 다를 수 있습니다 — 둘 중 하나가 틀린 것이 아니라, 그 구간에는 안정된 답이 없습니다.

5. 오늘 만든 신경망과 실제로 쓰이는 딥러닝의 거리를 세 가지(데이터 규모 · 층 수 · 가중치 수)로 쓰고, 그럼에도 같은 것은 무엇인지 한 문장으로 쓰세요.
📖 모범 답안

데이터 168일치 ↔ 수십억∼수조 개의 예시 / 층 수 2층(은닉 1층) ↔ 수십∼수백 층 / 가중치 19개 ↔ 수십억∼수조 개. 학습 시간도 1초 남짓 ↔ 수천 대의 GPU로 수 주입니다.

같은 것: 고리의 모양입니다 — 예측하고 → 오차를 재고 → 책임만큼 가중치를 고친다를 반복하는 것. 9차시의 경사하강법에서 시작해 14차시의 역전파로 이어진 그 고리가, 수십억 개짜리 모델에서도 똑같이 돌아갑니다.

그러므로 오늘 배운 것은 “나는 딥러닝을 만들 줄 안다”가 아니라 “원리는 같고 규모가 다르다”입니다. 두 문장은 아주 다릅니다.

6. 이 문제를 8차시의 k-NN으로 풀면 어떻게 될지 먼저 예측해 적은 뒤, 실제로 돌려 비교하세요. 특히 k = 1의 훈련 정확도가 얼마일지 예측하고, 왜 그런지 설명하세요.
📖 모범 답안

k = 1의 훈련 정확도는 100.0%입니다. 훈련 데이터의 한 점에서 가장 가까운 이웃은 자기 자신이고, 거리가 0이니까요. 자기 이름표를 그대로 베껴 답하는 셈입니다. 그런데 테스트는 69.4%로 기준선(65.3%)보다 겨우 4.1%포인트 높습니다. 격차 +30.6%포인트 — 오늘 본 것 중 가장 큰 격차이고, 과적합이 무엇인지 한 숫자로 보여 줍니다.

전체 비교: k = 3 → 76.4% / k = 5 → 77.8% / k = 9 → 77.8% / k = 15 → 79.2%(최고, 네 칸 20 / 10 / 5 / 37) / k = 25 → 73.6%. 신경망(80.6%, 최고 86.1%)이 k-NN(79.2%)보다 높습니다.

다만 단정하면 안 됩니다. 80.6%와 79.2%의 차이는 테스트 72일에서 단 한 날입니다. “신경망이 이겼다”가 아니라 “두 방법이 비슷했고, 둘 다 기준선보다 14∼15%포인트 나았다”가 정직한 보고입니다. 확실히 말할 수 있는 차이는 은닉 1개(68.1%)와 은닉 3개(80.6%) 사이의 12.5%포인트 — 이 데이터에 XOR과 같은 ‘어긋남’이 들어 있다는 증거입니다.

🔁 되돌아보기

오늘 14차시의 코드를 한 글자도 고치지 않고 실생활 데이터에 붙여 학습시키고, 12차시의 네 칸으로 성능을 쟀습니다. 그리고 두 손실 곡선이 갈라지는 자리를 직접 찾았습니다. 공책을 덮기 전에 한 줄만 적으세요 — “오늘 내가 믿으면 안 된다고 배운 숫자 하나와, 왜 믿으면 안 되는지.” (예: “86.1%가 83.3%보다 낫다 — 두 날 차이라 우열을 못 가린다”)
다음 시간에는 사진을 다루는 도구를 봅니다. 다만 미리 말해 둘 것이 있습니다 — 거기에는 오늘 같은 ‘학습’이 없습니다. 필터의 숫자를 사람이 정하거든요.

🔎

더 알아보기

오늘 잰 숫자를 한 걸음 더 — 언제 멈출지, 문턱을 얼마로 둘지, 그 점수를 얼마나 믿을지

훈련 손실 검증 손실 인내 구간 멈춤 ↑ 여기 가중치를 저장 ↑ 멈춘 곳 반복 멈춘 뒤 검증 손실이 가장 낮았던 가중치로 되돌린다. 테스트 데이터는 이 과정 내내 열어 보지 않는다.
생각할 거리

386회는 테스트를 보고 찾은 숫자다 — 그래서 실제로는 조기 종료를 쓴다

5절에서 은닉 3개의 테스트 손실이 386회에서 바닥을 친다는 것을 찾고, “여기서 멈췄어야 했다”고 했습니다. 그런데 그 자리를 테스트 데이터를 보며 찾았다는 점을 짚어 둬야 합니다. 테스트 점수를 보고 멈출 곳을 고르는 것은 11차시의 ‘테스트를 보고 고른 k’와 같은 일이에요. 테스트가 설정을 고르는 데 한 번 쓰이면, 그 점수는 더 이상 처음 보는 문제에 대한 점수가 아닙니다.

그래서 실제로는 훈련 데이터에서 검증 몫을 따로 떼어 두고, 한 바퀴 돌 때마다 검증 손실을 잽니다. 검증 손실이 가장 낮았던 때의 가중치를 저장해 두고, 정해 둔 횟수(인내, patience)만큼 더 돌아도 나아지지 않으면 학습을 멈추고 저장본으로 되돌립니다. 이것을 조기 종료(early stopping)라고 하며, 케라스(Keras) 같은 딥러닝 도구에는 기본 기능으로 들어 있습니다.

조기 종료는 과적합을 막는 가장 값싼 방법입니다. 모델도, 데이터도 바꾸지 않고 언제 그만둘지만 정하니까요. 다만 손실 곡선은 대개 매끄럽지 않아서, 인내를 너무 짧게 잡으면 잠깐 올랐다 다시 내려가는 흔들림에 속아 일찍 멈춥니다. 인내의 길이 역시 검증 데이터로 정할 설정값입니다.

문턱 = FP의 대가 ÷ (FP의 대가 + FN의 대가) 0 1 FN이 4배 아프다 0.2 똑같이 아프다 0.5 FP가 4배 아프다 0.8 급식실 표에 FP 한 번 = 4, FN 한 번 = 1로 놓으면 0.5 13×4 + 1 = 53 0.7 5×4 + 4 = 24 0.8 2×4 + 6 = 14 ← 가장 작다 막대 = 손해(FP 수×4 + FN 수×1)
원리 더 깊이

문턱을 얼마나 올릴까 — ‘몇 배 더 아픈가’가 답을 정한다

7절에서 급식실은 FP가 더 아프니 문턱을 0.5보다 올려야 한다고 했습니다. 얼마나 올릴지도 셈할 수 있습니다. 출력 o가 ‘오늘 많이 남을 확률’을 제대로 나타낸다고 해 봅시다. ‘많음’이라고 답하면 1−o의 확률로 틀려 FP의 대가를 치르고, ‘적음’이라고 답하면 o의 확률로 틀려 FN의 대가를 치릅니다. 두 쪽의 손해가 같아지는 자리가 바로 문턱 = FP의 대가 ÷ (FP의 대가 + FN의 대가)입니다.

밥이 모자라는 날(FP)이 버려지는 날(FN)보다 4배 아프다고 정하면 문턱은 4 ÷ 5 = 0.8입니다. 7절 표에 대 보면 손해는 문턱 0.5에서 53, 0.7에서 24, 0.8에서 14로, 표의 일곱 문턱 가운데 0.8이 가장 작습니다. 12차시에서는 기준점을 하나씩 전부 훑어 손해가 가장 작은 곳을 찾았지요. 출력이 확률이라면 식 한 줄이 그 자리를 곧장 짚습니다.

단서가 하나 있습니다. 이 식은 출력이 확률처럼 맞춰져 있어야 정확합니다 — 0.8이라고 답한 날들 가운데 실제로 80% 안팎이 많이 남아야 한다는 뜻입니다. 이렇게 맞추는 일을 보정(calibration)이라고 하는데, 오늘 신경망은 보정까지 하지는 않았습니다. 그래서 현장에서는 식으로 후보를 잡은 뒤 검증 데이터의 표로 다시 확인합니다. 그리고 ‘4배’를 정하는 일은 여전히 코드가 아니라 사람의 몫입니다.

ITU 표시가 붙은 연단 앞에서 마이크를 두고 발표하는 페이페이 리
역사

시험지를 새로 만들어 다시 채점하면 — 이미지넷 재시험

사진은 이미지넷(ImageNet)을 이끈 연구자 페이페이 리(Fei-Fei Li)입니다. 2009년에 공개된 이미지넷은 인터넷에서 모은 사진 1,400만 장 남짓에 사람이 하나하나 이름표를 붙인 데이터로, 이름표 붙이는 일은 온라인으로 일감을 나눠 받는 수많은 작업자가 맡았습니다. 여기서 1,000개 범주를 떼어 낸 대회가 2010년부터 열렸고, 14차시에서 본 2012년의 우승도 이 대회에서 나왔습니다.

그런데 여러 해 동안 모두가 같은 시험지로 점수를 겨뤘다면, 그 점수는 얼마나 믿을 만할까요? 2019년 한 연구진이 이미지넷을 처음 만들 때와 같은 절차로 새 시험지를 모아, 이미 나와 있던 여러 모델을 다시 채점했습니다. 결과는 뜻밖이었습니다. 시험한 모델들의 정확도가 11~14%포인트씩 떨어졌습니다. 다만 모델 사이의 순위는 대체로 그대로였어요.

연구진은 떨어진 까닭을 ‘같은 시험지를 거듭 본 탓’보다는 새로 모은 사진이 조금 더 어려웠기 때문으로 보았습니다. 같은 절차로 모아도 시험지가 조금만 달라지면 숫자가 크게 움직인다는 뜻입니다. 오늘 분할 씨앗만 바꿔도 테스트 정확도가 61.1%에서 81.9%까지 흔들렸던 것과 같은 이야기지요. 그래서 “80.6%”는 모델의 점수라기보다 이 모델이 그 72일에 받은 점수로 읽어야 합니다. 이미지넷 시험지는 만 장 단위라 한두 장 차이로 순위가 바뀌지 않지만, 테스트가 72일뿐인 오늘은 두 날 차이(86.1%와 83.3%)로 우열을 가릴 수 없다는 것도 함께 기억해 두세요.

사진: 2017년 AI for Good 행사에서 발표하는 페이페이 리 · 출처: ITU Pictures, Wikimedia Commons (CC BY 2.0)