자가 진단과 역량 지도

먼저 읽을 장 0.3 학습 경로

0.3장의 경로는 "이미 아는 것은 건너뛰고 빈칸을 먼저 채운다"는 전제 위에 있다. 그런데 무엇을 이미 아는지는 생각보다 판단하기 어렵다. 들어 본 개념은 아는 것처럼 느껴지고, 써 본 라이브러리는 이해한 것처럼 느껴진다. 이 느낌대로 경로를 짜면 정작 비어 있는 칸을 건너뛰고, 이미 채워진 칸을 다시 공부하게 된다.

이 장은 느낌 대신 근거로 자신의 위치를 진단하는 도구를 만든다. 네 축과 다섯 수준으로 이루어진 역량 지도, 축마다 직접 풀어 보는 진단 문항, 그리고 진단 결과를 학습 경로로 옮기는 방법이다. 진단 문항 일부는 브라우저에서 바로 실행하는 코드다.

학습 목표

  • 역량 지도로 현재 수준을 진단한다
  • 진단 결과를 학습 경로에 반영한다

역량 지도

왜 필요한가. "머신러닝을 어느 정도 안다"는 말은 경로를 정하는 데 쓸 수 없다. 역전파를 설명할 수 있는지, 구현할 수 있는지, 구현이 맞는지 확인할 수 있는지는 전혀 다른 수준이고, 각각 다른 장으로 채운다. 역량 지도는 이 차이를 칸으로 나눠, 비어 있는 칸이 어느 장으로 이어지는지 보이게 한다.

정의. 역량 지도는 0.1장의 네 축(이론·구현·연구·운영)을 행으로, 다섯 수준을 열로 둔 표다.

수준 이름 뜻
1 인지 이름과 쓰임새를 안다. 무엇을 검색해야 할지 안다
2 설명 정의와 가정을 자기 말과 수식으로, 자료 없이 설명한다
3 구현 직접 코드나 절차로 옮겨 동작하게 만든다
4 검증 구현과 결과가 맞는지 독립적인 방법으로 확인한다
5 확장 새 문제에 맞게 변형하고, 한계를 밝히고, 남에게 가르친다

수준은 누적된다. 검증(4)에 있다는 것은 설명(2)과 구현(3)도 할 수 있다는 뜻이다. 아무것도 모르면 0으로 둔다.

직관. 같은 주제라도 축마다 수준이 다를 수 있다. 역전파를 예로 들면 다음과 같다.

축 1 인지 2 설명 3 구현 4 검증 5 확장
이론 역전파라는 이름과 용도를 안다 2층 MLP의 gradient를 연쇄법칙으로 종이에 유도한다 유도한 식을 NumPy로 구현한다 구현한 gradient를 수치미분과 비교해 오차를 보고한다 새 층의 gradient를 유도·구현·검증하고 남에게 설명한다
구현 PyTorch 학습 루프의 구성 요소를 안다 각 단계가 하는 일과, gradient를 초기화하지 않으면 무엇이 일어나는지 설명한다 데이터 로드부터 평가까지 학습 스크립트를 처음부터 작성한다 작은 batch 하나에 과적합시키기, 고정 seed, 테스트로 동작을 확인한다 설정·CLI를 갖춘 템플릿으로 만들어 다른 문제에 재사용한다
연구 baseline과 ablation이 무엇인지 안다 논문의 주장과 그를 뒷받침하는 실험을 대응시켜 요약한다 가설·비교 대상·metric·분할을 정한 실험 계획서를 쓴다 여러 seed와 공정한 튜닝으로 차이가 잡음보다 큰지 확인하고 주장의 범위를 정한다 독립 연구 질문을 세우고 결과를 논문 초안으로 쓴다
운영 드라이버·CUDA·Python 패키지의 계층을 안다 GPU를 찾지 못하는 오류의 가능한 원인을 계층별로 설명한다 원격 서버에 환경을 만들고 학습을 세션이 끊겨도 돌게 실행한다 lockfile로 새 환경을 다시 만들어 같은 결과를 얻는다 다른 사람이 쓸 수 있는 컨테이너와 문서로 공유한다

근거 규칙. 칸에 수준을 적을 때는 반드시 근거를 함께 적는다. 근거는 자신이 만든 것이다: 코드 파일, 통과한 테스트, 유도를 적은 노트, 계획서, 재현 기록. 근거를 댈 수 없는 수준은 그 아래 수준으로 적는다. 이 규칙이 역량 지도를 느낌의 기록이 아니라 진단 도구로 만든다.

축 주제 수준 근거
(예) 이론 gradient와 역전파 3 notes/backprop.md의 유도, mlp_numpy.py
(예) 운영 원격 GPU 서버 1 (없음 — 접속해 본 적 없음)

들어 본 것과 설명할 수 있는 것. 가장 흔한 진단 오류는 1 인지를 2 설명으로 적는 것이다. 개념을 여러 번 들으면 익숙해지고, 익숙함은 이해처럼 느껴진다. 이 착각은 실제로 설명을 써 보기 전까지 드러나지 않는다. 그래서 2 이상의 수준을 적기 전에 다음 순서를 지킨다.

  1. 자료를 보지 않고, 그 개념을 처음 듣는 동료에게 설명하듯 3–5문장과 필요한 수식을 쓴다.
  2. 쓴 설명을 해당 장의 학습 목표와 비교한다. 빠진 가정이나 틀린 식이 있는지 본다.
  3. 그다음에 수준을 적는다. 설명을 쓰다 막혔다면 1로 적는다.

이렇게 하면 처음 매긴 수준이 내려가는 경우가 많다. 내려간 것은 실패가 아니라 진단이 제대로 동작한 것이다.

연구에서의 의미. 연구에서 문제가 생겼을 때 원인을 찾는 범위는 자신이 검증(4) 수준에 있는 축으로 좁혀진다. 학습이 실패했을 때 운영 축이 1이면, 환경 문제를 의심할 생각 자체를 하지 못한다. 역량 지도의 빈칸은 공부할 목록이기도 하지만, 지금 자신이 원인을 찾지 못하는 영역의 목록이기도 하다.

진단 문항

축마다 직접 풀어 보는 문항이다. 모든 문항을 풀 필요는 없다. 자신이 2 이상이라고 생각하는 칸만 골라 확인한다. 각 문항 뒤에는 그 칸을 채우는 장을 적었다.

이론: 선형대수·확률·최적화

코드 문항은 실행하기 전에 결과를 예측한다. 예측이 맞았는지보다, 예측의 근거를 말할 수 있는지가 진단의 대상이다.

선형대수. (5, 2) 행렬과 (2, 4) 행렬을 곱하면 (5, 4) 행렬이 된다. 이 행렬의 rank는 얼마일까.

import numpy as np

rng = np.random.default_rng(0)
U = rng.normal(size=(5, 2))     # (5, 2)
V = rng.normal(size=(2, 4))     # (2, 4)
A = U @ V                       # (5, 4)
print("shape:", A.shape, " rank:", np.linalg.matrix_rank(A))
shape: (5, 4)  rank: 2

A=UV\mathbf{A} = \mathbf{U}\mathbf{V}의 모든 열은 U\mathbf{U}의 두 열의 선형결합이므로, 열공간의 차원은 2를 넘지 못한다: rank⁡(UV)≤min⁡(rank⁡U,rank⁡V)\operatorname{rank}(\mathbf{U}\mathbf{V}) \le \min(\operatorname{rank}\mathbf{U}, \operatorname{rank}\mathbf{V}). 행렬이 (5, 4)라는 shape만 보고 rank 4를 예측했다면 7.3장(행렬의 네 부분공간과 rank)이 다음 장이다. 이 사실은 저랭크 근사(7.6장)와 LoRA 같은 저랭크 미세조정의 출발점이다.

같은 축에서 종이로 확인할 질문:

  • 대칭행렬의 고유벡터는 왜 직교하게 고를 수 있는가? (7.5장)
  • 최소제곱해가 사영이라는 것은 무슨 뜻인가? (7.4장)
  • SVD의 세 행렬은 각각 기하학적으로 무엇을 하는가? (7.6장)

미분과 최적화. 평균 제곱 오차 L(w)=1N∥Xw−y∥2L(\mathbf{w}) = \frac{1}{N}\lVert \mathbf{X}\mathbf{w} - \mathbf{y} \rVert^2의 gradient를 자료 없이 유도할 수 있는가? 유도한 식이 ∇wL=2NX⊤(Xw−y)\nabla_{\mathbf{w}} L = \frac{2}{N}\mathbf{X}^\top(\mathbf{X}\mathbf{w} - \mathbf{y})와 같다면, 다음으로 그 식이 맞는지 검증할 수 있는지 본다. 아래 코드는 임의의 방향 v\mathbf{v}에 대해 방향 미분 ∇L⊤v\nabla L^\top \mathbf{v}를 중심 차분 (L(w+hv)−L(w−hv))/2h\big(L(\mathbf{w} + h\mathbf{v}) - L(\mathbf{w} - h\mathbf{v})\big)/2h와 비교한다.

N, D = 50, 3
X = rng.normal(size=(N, D))                  # (N, D)
y = X @ np.array([1.0, -2.0, 0.5]) + 0.1 * rng.normal(size=N)   # (N,)

def loss(w):                                 # w: (D,)
    r = X @ w - y                            # (N,)
    return r @ r / N

def grad(w):
    return 2.0 / N * X.T @ (X @ w - y)       # (D,)

w = rng.normal(size=D)                       # (D,)
v = rng.normal(size=D)                       # (D,) 임의의 방향
h = 1e-5
numeric = (loss(w + h * v) - loss(w - h * v)) / (2 * h)
print(f"grad·v = {grad(w) @ v:.8f}")
print(f"수치   = {numeric:.8f}")
grad·v = 3.81700067
수치   = 3.81700067

두 값이 소수점 아래 여덟째 자리까지 같다. 유도는 했지만 이런 확인을 해 본 적이 없다면 이론 축의 미분 칸은 3이 아니라 2다. 이 장의 연습문제(실습 절)가 좌표마다 수치미분을 하는 검증 도구를 직접 만드는 과제다. 막히면 8.1장(다변수 미분)과 8.4장(자동미분과 역전파)으로 간다.

같은 축에서 종이로 확인할 질문:

  • 학습률이 너무 크면 이차 손실에서 경사하강이 왜 발산하는가? (10.3장)
  • 볼록 함수에서 국소 최솟값이 전역 최솟값인 이유는? (10.1장)

확률과 통계. 표준편차가 σ=2\sigma = 2인 분포에서 독립 표본 25개를 뽑아 평균을 낸다. 이 실험을 여러 번 반복하면, 표본 평균들의 표준편차는 얼마일까.

sigma, n, trials = 2.0, 25, 20_000
samples = rng.normal(0.0, sigma, size=(trials, n))   # (trials, n)
means = samples.mean(axis=1)                         # (trials,)
print(f"표본 평균들의 표준편차 = {means.std():.4f}")
print(f"sigma / sqrt(n)       = {sigma / np.sqrt(n):.4f}")
표본 평균들의 표준편차 = 0.3994
sigma / sqrt(n)       = 0.4000

독립인 확률변수의 합의 분산은 분산의 합이므로 Var⁡[Xˉ]=σ2/n\operatorname{Var}[\bar{X}] = \sigma^2 / n이고, 표준편차는 σ/n=2/5=0.4\sigma/\sqrt{n} = 2/5 = 0.4다. 시뮬레이션 값 0.3994는 이 값 근처에서 흔들린다. 2를 답했다면 개별 측정의 흩어짐과 평균의 불확실성을 구분하지 못한 것이고, 11.5장(기댓값·부등식·극한정리)과 12.1장(추정량의 성질)이 다음 장이다. 이 구분은 실험 결과에 오차 막대를 붙일 때마다 쓰인다. 오차 막대가 표준편차인지 표준오차인지 밝히지 않은 그림은 해석할 수 없다.

같은 축에서 종이로 확인할 질문:

  • 조건부확률 p(A∣B)p(A \mid B)와 p(B∣A)p(B \mid A)가 다른 예를 들 수 있는가? (11.3장)
  • 제곱손실과 절대손실로 학습한 회귀 모델은 각각 조건부분포의 무엇을 추정하는가? (12.3장)

구현: 환경 구축부터 학습·평가까지

다음 과제를 새 폴더에서, 자료를 최소한으로 보며 처음부터 끝까지 해 본다. 막힌 단계가 구현 축의 빈칸이다.

단계 과제 막히면
1 격리된 Python 환경을 만들고 필요한 패키지를 설치한다 3.3장
2 공개 표 데이터(예: scikit-learn에 포함된 회귀 데이터)를 불러와 shape과 결측을 확인한다 5.3장, 15.3장
3 학습·검증·테스트로 나누고, 전처리 통계는 학습 데이터에서만 계산한다 15.4장
4 평균 예측과 선형 회귀 baseline을 만든다 16.1장, 16.2장
5 PyTorch로 작은 MLP를 학습한다 17.2–17.5장
6 같은 분할·같은 metric으로 세 모델을 비교한 표를 저장한다 16.6장
7 다른 사람이 명령 하나로 같은 표를 다시 얻을 수 있게 정리한다 1.5장, 22.2장

검증(4) 수준을 확인하는 과제는 따로 있다. 작은 batch 하나에 모델을 일부러 과적합시켜 학습 손실이 0 가까이 내려가는지 본다. 내려가지 않으면 데이터나 모델이 아니라 학습 루프 자체에 버그가 있다는 신호다(17.9장). 이 확인을 해 본 적이 있는가?

연구: 논문 요약과 실험 설계

연구 축은 문서로 진단한다.

  • 논문 요약 과제. 자기 분야의 ML 논문 하나를 골라 한 쪽으로 요약한다. 문제 정의, 가정, 비교 대상(baseline), 평가 조건(데이터·분할·metric), 그리고 주장마다 그것을 뒷받침하는 실험을 표로 대응시킨다. 대응되는 실험이 없는 주장을 찾아낼 수 있으면 설명(2)을 넘어선 것이다. (23.2장)
  • 실험 설계 과제. 0.1장 실습에서 쓴 연구 문단을 한 쪽짜리 실험 계획서로 바꾼다. 가설, 비교 대상, 분할 방식, metric, seed 수, 그리고 "어떤 결과가 나오면 가설이 틀렸다고 판단할지"를 적는다. 마지막 항목을 쓸 수 없다면 구현(3) 수준이 아직 아니다. (23.4장, 23.5장)

운영: 서버·Git·재현성

운영 축은 터미널에서 진단한다. 다음 명령은 집필 시점의 예시다. 명령 자체보다, 출력을 보고 무엇을 확인했는지 말할 수 있는지가 진단의 대상이다.

# GPU와 드라이버가 보이는가, 지금 누가 GPU를 쓰고 있는가 (2.2장)
nvidia-smi

# 설치된 PyTorch가 GPU를 쓸 수 있는가, 어떤 CUDA로 빌드되었는가 (3.1장)
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"

# 이 결과를 만든 코드는 어느 커밋인가, 커밋하지 않은 변경이 있는가 (4.1장)
git log --oneline -5
git status --short
과제 수준 막히면
SSH 키로 원격 서버에 접속하고, 접속이 끊겨도 학습이 계속 돌게 실행한다 3 2.2장, 2.3장
torch.cuda.is_available()이 False일 때 원인을 드라이버·CUDA·패키지 계층에서 하나씩 좁힌다 4 3.1장, 3.6장
lockfile로 다른 기계에 같은 환경을 다시 만들고 같은 숫자를 얻는다 4 3.4장, 22.2장
결과 표의 숫자에서 그 숫자를 만든 커밋·설정·데이터 버전으로 되돌아간다 4 22.1장

진단 결과의 활용

가장 낮은 칸부터. 역량 지도를 채우면 칸마다 수준이 생긴다. 이 중 자기 연구에 필요하면서 가장 낮은 두 칸을 고른다. 가장 높은 칸을 더 높이는 것은 즐겁지만, 연구를 막는 것은 대개 가장 낮은 칸이다. 학습 손실이 이상할 때 이론이 4이고 운영이 1이면, 원인이 환경에 있을 경우 찾지 못한다.

칸에서 장으로. 고른 두 칸을 다음 표로 장에 연결하고, 0.3장의 3개월 경로에 반영한다.

빈칸 먼저 볼 장
이론: 선형대수 7.1–7.6장
이론: 미분·최적화 8.1장, 8.4장, 10.1장, 10.3장
이론: 확률·통계 11.1–11.5장, 12.1–12.3장
구현: Python·과학 계산 5.1장, 5.3장
구현: 연구 코드 구조와 테스트 6.1장, 6.3장, 6.6장
구현: 학습 파이프라인 1.5장, 17.2–17.6장, 17.9장
연구: 논문 읽기와 재현 23.2장, 23.3장
연구: 가설과 실험 설계 23.4–23.6장
운영: 서버와 셸 2.1–2.3장
운영: 환경과 재현성 3.3–3.6장, 22.1–22.2장

정기적 재진단과 성장 기록. 역량 지도는 한 번 쓰고 끝나는 문서가 아니다. 학습 경로의 단계를 마칠 때마다, 또는 3개월마다 다시 채운다. 이전 지도는 지우지 않고 날짜를 붙여 남긴다. 칸이 바뀔 때마다 다음 한 줄을 성장 기록에 더한다.

2026-12-10 | 운영: 환경과 재현성 | 1 → 4 | 근거: repro/README.md, 다른 서버에서 같은 표 재생성

성장 기록은 두 가지에 쓰인다. 첫째, 경로를 고칠 근거가 된다. 3개월 동안 칸이 움직이지 않았다면 경로나 학습 방식을 바꿔야 한다. 둘째, 31.3장(역량 평가와 다음 단계)에서 처음 지도와 마지막 지도를 비교하는 재료가 된다.

실습

역량 지도를 채우고, 가장 낮은 두 칸을 끌어올릴 장을 고른다.

  1. 4 × 5 역량 지도를 문서로 만든다. 축마다 자기 연구에 필요한 주제를 2–3개씩 적는다(예: 이론 — 선형대수, 확률, 최적화).
  2. 주제마다 수준과 근거를 적는다. 2 이상을 적기 전에 "들어 본 것과 설명할 수 있는 것"의 세 단계(설명 쓰기 → 학습 목표와 비교 → 수준 매기기)를 거친다.
  3. 이 장의 진단 문항 중 자신이 2 이상이라고 적은 칸에 해당하는 것을 푼다. 결과에 따라 수준을 고친다.
  4. 연습문제를 푼다. 이론·구현 축의 "검증" 수준을 확인하는 과제다.
  1. 자기 연구에 필요하면서 가장 낮은 두 칸을 고르고, 각 칸을 채울 장과 산출물을 0.3장의 3개월 경로에 넣는다.
  2. 성장 기록 파일을 만들고 오늘 날짜로 첫 줄을 쓴다.

실패 사례

  • '들어본 적 있다'를 '설명할 수 있다'로 착각하는 것. 익숙한 개념일수록 수준을 높게 매기기 쉽다. 설명을 직접 써 보지 않고 매긴 수준은 대개 한 단계 이상 높다. 그 결과 경로에서 정작 필요한 장을 건너뛴다.
  • 라이브러리를 써 본 것을 구현으로 적는 것. model.fit()을 호출해 결과를 얻은 것은 그 알고리즘을 구현한 것이 아니다. 구현 축의 수준은 자신이 작성한 코드에 대해 매기고, 이론 축의 수준은 라이브러리가 하는 일을 수식으로 설명할 수 있는지로 매긴다.
  • 근거 없는 진단. 근거 칸을 비워 둔 역량 지도는 3개월 뒤 다시 채울 때 비교할 수 없다. 무엇이 바뀌었는지 모르면 경로를 고칠 근거도 없다.
  • 가장 높은 칸만 키우는 것. 이미 잘하는 축의 심화 장은 읽기 즐겁지만, 연구를 막는 것은 가장 낮은 칸이다.

통과 기준

  • 4 × 5 역량 지도를 문서로 작성한다. 축마다 주제 2개 이상, 칸마다 수준과 근거(만든 코드·노트·문서의 경로)가 있다.
  • 연습문제 numerical-gradient의 테스트를 통과한다.
  • 자기 연구에 필요하면서 가장 낮은 두 칸과, 그 칸을 채울 장·산출물을 0.3장의 학습 경로 문서에 반영한다.
  • 성장 기록 파일에 오늘 날짜의 첫 줄을 남긴다.