실습 · 연습문제 2개
MLP 학습 한 step: forward → loss → backward → update mlp-train-step
본문의 작은 MLP를 학습하는 한 step을 직접 쓴다. 함수 train_step(params, X, y, lr)를 완성해 갱신된 파라미터와 갱신 전 파라미터에서의 loss를 반환한다.
모델은 은닉층 하나(tanh)의 MLP다.
| 이름 | shape |
|---|---|
X |
(N, D) |
y |
(N, 1) |
params["W1"], params["b1"] |
(D, H), (H,) |
params["W2"], params["b2"] |
(H, 1), (1,) |
순서는 다음과 같다.
- forward: 와 를 계산한다.
- loss: 위의 MSE 을 계산한다.
- backward: 연쇄법칙으로 네 파라미터의 gradient를 구한다. 이고, 다.
- update: 각 파라미터 를 로 바꾼다.
반환값은 (new_params, loss)이고, new_params는 같은 키를 가진 새 dict다. 입력으로 받은 params의 배열을 제자리에서 바꾸지 않는다.
테스트는 loss 값, gradient(중심 차분과 비교), 반복할 때 loss가 줄어드는지, 입력을 바꾸지 않는지를 확인한다. gradient는 (params - new_params) / lr로 복원해 비교한다.
다시 실행해도 같은 분할 reproducible-split
"다른 사람이 같은 명령으로 같은 숫자를 다시 얻는다"의 첫 조건은 같은 분할이다. 함수 split_indices(n, fractions, seed)를 완성해 학습·검증·테스트 인덱스 배열 세 개를 반환한다.
fractions = (f_train, f_val, f_test)이고 합은 1이다.- 크기는
n_train = int(f_train * n),n_val = int(f_val * n), 나머지는 모두 테스트다. 따라서 세 배열을 합치면0, 1, …, n-1이 빠짐없이, 겹치지 않게 들어 있다. 0, …, n-1을 무작위로 섞은 뒤 앞에서부터 잘라 나눈다.- 무작위성은
np.random.default_rng(seed)로 만든 자기만의 난수 생성기에서만 얻는다. 전역 난수 상태(np.random.seed,np.random.permutation)를 쓰지도, 바꾸지도 않는다.
테스트는 다음을 확인한다.
- 크기, 겹침 없음, 빠짐없음
- 같은
seed이면 같은 분할, 다른seed이면 다른 분할 - 함수 밖에서
np.random.seed(...)를 바꾸거나 다른 난수를 먼저 뽑아도 결과가 같은지 - 함수를 불러도 전역 난수 상태가 바뀌지 않는지
3–4번이 중요한 이유: 전역 난수 상태에 기대는 코드는 앞에서 어떤 코드가 몇 번 난수를 뽑았는지에 따라 분할이 달라진다. 셀 실행 순서나 import 순서만 바뀌어도 숫자가 바뀐다.