Index
2026-08-19 — Engineering

exp_matrix 결정적 과제 샘플링 (--seed-base, Task N11)

scene_bench | src/sbench_online/exp_matrix.py, commit 5c13c59

TL;DR

+72/-5
exp_matrix.py
+106
신규 test 줄수
2
검증 house (136/290)

1 배경 / 목적

260817 260817-multiscene_ckpt_matrix.html에서 part1 vs aux-mix ckpt를 비교했지만 pick 성공률이 2/5로 동률이 나와 "aux mix가 실제로 개선인지"를 판정할 수 없었다 — n=1/셀이라 방향성만 확인 가능한 수준으로 보고했다. 근본 원인은 N8 §5에서 이미 실증된 것: --seed를 줘도 sample_task()의 재현 가능성은 sampler 내부 RNG에 의존해 보장되지 않는다(exp_matrix.py 모듈 docstring 구 버전 명시) — 즉 ckpt A와 ckpt B를 같은 (house, skill, idx) 행에서 돌려도 서로 다른 물체 쌍이 샘플링돼, SR 차이가 정책 차이인지 과제 난이도 차이인지 구분할 수 없었다.

기존 한계: 표본 수(--samples)를 늘려 평균내는 방식으로만 노이즈를 흡수했는데, 이는 비용(런타임)을 n배로 키울 뿐 근본 해결이 아니다.

2 작업 내용

MolmoSpaces 소스($MS/molmo_spaces/tasks/task_sampler.py)를 먼저 읽고 재현성이 왜 깨지는지 정확한 지점을 특정했다:

TaskSampler.__init__ (268행) 312-314행: seed = self.config.seed if self.config.seed is not None else np.random.randint(0, 100000000) self.seed_task_sampling(seed) # 419-423행: random.seed/np.random.seed/torch.manual_seed make_cfg()가 만드는 cfg.seed는 기본값 None(실측 확인) → 매번 np.random.randint() 분기를 탄다 → 이 draw 자체가 "호출 시점의 numpy 전역 RNG 상태"에서 결정적으로 나옴 → sampler 생성자 직전에 np.random.seed(seed)를 걸면 재시드 체인 전체가 결정적이 됨

이 분석을 바탕으로 _stable_seed(house, skill, idx, seed_base) 함수를 추가했다. 파이썬 내장 hash()는 str에 대해 PYTHONHASHSEED salt로 프로세스마다 값이 달라지는데, part1 잡과 mixv2 잡은 서로 다른 sbatch worker 프로세스라 hash()를 쓰면 같은 (house,skill,idx)에서도 다른 시드가 나와 쌍 비교가 깨진다 — 그래서 입력 바이트에만 의존해 프로세스 간 항상 동일한 값을 내는 zlib.crc32를 택했다.

def _stable_seed(house, skill, idx, seed_base) -> int: key = f"{house}|{skill}|{idx}".encode() return (zlib.crc32(key) + seed_base) % (2**32) # run_matrix()의 매 (h, skill, s) 루프 안, sampler 생성 직전: if seed_base is not None: seed = _stable_seed(h, skill, s, seed_base) random.seed(seed); np.random.seed(seed) cfg = make_cfg(...) sampler = cfg.task_sampler_config.task_sampler_class(cfg) task = sampler.sample_task(house_index=h) # CLI: --seed-base N (기본값은 SEED_BASE 환경변수 폴백) # → run_matrix_worker.sh를 무수정으로 재사용해야 하는 제약 때문에 # sbm 제출 시 `export SEED_BASE=`으로도 넘길 수 있게 함

결과 row에는 seed 필드를(미지정 시 None, 하위호환), matrix_results.json 메타에는 seed_base를 추가해 어떤 시드로 돌았는지 기록에 남겼다.

3 결과 (수치)

항목수치비고
exp_matrix.py diff+72 / -5_stable_seed, CLI 플래그, run_matrix 시그니처 확장
신규 테스트tests/test_exp_matrix.py +106줄결정성 회귀 테스트
검증 house136, 290CPU-only(GPU/물리 시뮬 없이 sampler 레벨만 검증)
재현성동일 시드 → 동일 (pickup, receptacle) 쌍2회 호출 일치 확인
구분성다른 sample_idx → 다른 쌍같은 seed_base라도 idx가 다르면 다른 과제 샘플링 확인
확인: 소스 코드 분석(재시드 체인이 numpy 전역 상태에만 의존)과 CPU 실측이 정확히 일치 — 별도 몽키패치나 sampler 수정 없이 호출 시점 전역 RNG만 고정해도 충분함을 검증.

4 Takeaway

의미

260817 매트릭스 실험의 "n=1/셀이라 방향성만" 한계는 표본을 늘려서가 아니라 같은 과제 조건에서 ckpt만 바꿔 비교(paired comparison)하는 인프라가 없었던 게 근본 원인이었다. --seed-base는 그 인프라를 만든다 — 같은 값으로 두 ckpt를 각각 실행하면 (house,skill,idx)가 같은 행끼리 동일 물체 배치가 보장되므로, SR 차이를 순수하게 ckpt 차이로 귀속시킬 수 있다. 별도 GPU/시뮬 실행 없이 sampler 레벨 CPU 검증만으로 결정성을 확인했다는 점도 다음 실 매트릭스 실행 전 리스크를 줄여준다.

5 Next Steps

미해결 한계

이번 작업은 샘플링 결정성만 확보했고, 아직 seed-base를 적용한 실제 재실행은 하지 않았다 — part1 vs aux-mix의 "동률" 결과가 노이즈였는지 실제 무차이였는지는 재실행 전까지 알 수 없다. 또한 결정적 시드가 물리 시뮬레이터의 다른 non-determinism(렌더링, 물리 스텝 등)까지 고정하는지는 검증 범위 밖(sampler 레벨만 확인).

다음 실험

context.md에 기록된 대로 N11(재학습 mix 매트릭스)이 병행 진행 중 — --seed-base를 적용해 part1 vs aux-mix 쌍 비교를 재실행하고, 260817 결과(3/10 vs 2/10, pick 2/5 동률)가 노이즈였는지 재확인해야 한다. worker 제출 시 SEED_BASE 환경변수 경로도 함께 검증(run_matrix_worker.sh 무수정 제약 하의 실제 동작 확인).