본문 바로가기
C.W.K.
Stream
Lesson 02 of 04 · published

Seed 를 저장하면 결과를 소유하게 돼

~11 min · reproducibility, random-seed, determinism, audit

Level 0Open Gate
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"seed 를 저장해 둔 무작위는 더 이상 그냥 무작위가 아니야. 증명할 수 있는 결과지."

재현 가능한 무작위라는 역설

Monte Carlo 는 무작위 추첨 위에 서 있으니 언뜻 재현 가능성과는 정반대로 보여. 두 번 돌리면 부챗살이 두 개 나오잖아. Keep 는 run 마다 random seed 를 같이 저장해서 이 역설을 풀어. pseudo-random generator 는 사실 deterministic 이거든. 같은 seed 를 주면 어느 머신에서든 매번 완전히 같은 '무작위' 수열을 뱉어. 그러니 seed 까지 붙잡아 둔 run 은 나중에 bit 단위로 똑같이 되살릴 수 있어. 무작위성은 가능성을 탐색할 만큼 진짜인 동시에, 재현하고 감사할 수 있을 만큼 붙들려 있는 거야.

run 이 저장해 두는 것들

재현성이란 출력을 결정한 요소의 일부가 아니라 전부를 붙잡는 거야. Keep 의 simulation run 은 자기를 결정한 완전한 목록을 저장해. mode, owner, ticker set, horizon, iteration 수, hurdle, mean-reversion 계수, calibration, seed 가격, percentile path, 결과, 그리고 random seed 까지. 이 중 하나만 빠져도 재현성은 끝장이야. 같은 seed 라도 입력이 다르면 답이 달라지고, 입력이 같아도 seed 를 잃으면 매번 다른 무작위 path 가 나와. 재현성은 전부 아니면 전무야. 레시피를 완전하게 붙잡거나, 케이크를 다시 못 굽거나.

계산을 재현하려면 모든 입력을 저장해. seed 도 입력이야. 되살릴 수 없는 결과는 나중에 검증도 디버깅도 신뢰도 못 하는 결과야. random seed 는 다른 파라미터와 똑같은 자격의 입력인데, 이걸 일회용으로 취급하는 게 '재현 가능하다던' simulation 이 조용히 재현 불가능해지는 가장 흔한 경로야.

감사 가능한 run 이 여기서 중요한 이유

학문적 결벽 때문이 아니야. 앱 전체의 차분하고 정직한 자세를 받치는 장치야. run 이 통째로 저장되고 언제든 재생되니까, 몇 달 뒤에 그 시나리오로 돌아와도 무슨 가정으로 무슨 결과를 냈는지 정확히 확인할 수 있어. "예전에 비슷한 걸 돌렸던 것 같은데" 하는 흐릿한 기억이 아니라. UI 에 아무도 출처를 설명 못 하는 유령 숫자가 떠다니는 일도 없고. 모든 시나리오가 완전한 provenance 를 갖춘, 오래가고 들여다볼 수 있는 기록이야. Keep 가 가격과 현금에 들이대던 그 정직함을 simulation 에도 똑같이 들이댄 거지.

삭제까지 정밀해. run 은 저마다의 numeric primary key 로 식별되고, 삭제도 정확히 그 key 하나를 겨냥해. 비슷한 걸 쓸어 담을지도 모르는 흐릿한 매칭이 아니라, 특정 run 하나만 정확히 제거하는 거야. 만들 때도 정밀한 식별, 지울 때도 정밀한 식별. run 은 태어나서 사라질 때까지 하나하나 주소를 가진 일급 기록이야.

Code

seed 가 '무작위' 를 재현 가능하게 만든다 (예시)·python
import numpy as np

def run_simulation(inputs):
    seed = inputs.get("seed") or new_seed()   # 한 번 고르고, 그대로 저장
    rng = np.random.default_rng(seed)         # seed 가 정하는 deterministic 수열
    result = simulate(**inputs, rng=rng)
    persist_run({**inputs, "seed": seed, "result": result})  # 전부 저장
    return result

# 재생은 정확해. 같은 입력 + 같은 seed -> 같은 부챗살.
def replay(run):
    rng = np.random.default_rng(run["seed"])  # 같은 seed
    return simulate(**run["inputs"], rng=rng)  # bit 단위로 같은 결과

# assert replay(run) == run['result']   # 언제까지나, 어느 머신에서든

External links

Exercise

무작위성이 들어간 계산을 아무거나 찾아 봐. shuffle 이든 sampling 이든 simulation 이든 합성 데이터셋이든. 오늘 그때와 완전히 같은 출력을 다시 만들어 낼 수 있어? 그러려면 뭘 저장해 뒀어야 했는지 전부 나열해 보고, 그 목록에 seed 가 있었는지 확인해 봐. 그다음 seed 가 없어서, 누가 물어보는 결과를 재현하지 못하는 디버깅 상황을 하나 그려 봐.
Hint
고전적인 낭패는 이거야. "지난주에 이상한 결과 나왔다며, 재현해 줄래?" 근데 못 해. seed 를 저장한 적이 없어서 다시 돌릴 때마다 새로운 무작위가 나오니까. seed 를 저장하는 순간 "비슷한 걸 했던 것 같은데" 가 "여기, 정확히 그 run 을 다시 돌렸어" 로 바뀌어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.