松田研究室
FindingID: p3-refine-every-200
Status: stable / task docs/findings/p3-c2-bubble-attribution.toml §6 施策 #5 (refine.every 100→200)
← Findings Index
docs/findings/p3-c2-bubble-attribution.toml §6 施策 #5 · refine.every 100→200

refine.every 100→200 — 不採用 (PSNR guard 大幅超過、2026-08-30 gate)

docs/findings/p3-c2-bubble-attribution.toml §6 施策 #5 — refine tick (100 iter に 1 回) の CPU compact drain が 12.5k で 3.0〜3.8s (2.4〜3.1%) を占めることから、tick 頻度を半減すれば drain も半減し wallclock -1.2〜-1.5% を期待した hyperparameter 実験。<strong>12.5k n=3 交互 A/B で PSNR Δ -0.421dB (guard `on mean >= off mean - 0.05dB` を約 8.4 倍超過) — 不採用</strong>。wallclock 自体は Δ -25.6% と大幅に短縮したが、これは drain 削減 (メカニズムは的中、sync_iters 121→61 で確認、寄与は期待帯 -1.2〜-1.5% 相当) ではなく、tick 頻度半減が同時に densification 予算を削り最終 splat 数を -35% (407.7k→266.7k) 減らしたことによる副作用が主因 — <code>refine.every</code> は drain のみを制御する独立ノブではなく、tick cadence に紐づく growth/clone/split イベント数と opacity decay 累積量を同時に変える hyperparameter であることが判明した。<strong>default config は変更しない、採否はユーザー判断待ち</strong>。

negativerank: lowrejected第 1 軸第 3 軸p3-refine-every-200re200refinehyperparameterpsnr-guard-faildensification-couplinglegogate-rejectedcontaminated-pair-retry
Δ PSNR
**-0.421 dB** (12.5k n=3、guard `on mean >= off mean - 0.05dB` を約 8.4 倍超過 — 不通過)。3/3 pair で一貫して on &lt; off (-0.361 / -0.544 / -0.397 dB)
Δ Wallclock
**-25.6%** (12.5k n=3、off mean 126.497s [off-r2 は load 汚染が retry 後も残存、参考値] → on mean 94.063s [on-r2 は retry で clean 値に置換]) — off の clean 2 値のみ (off-r1/r3 平均 124.92s) を使うと Δ **-24.7%**、いずれにせよ §6 施策 #5 が想定した drain 削減の期待帯 (-1.2〜-1.5%) を遥かに超える。分解すると drain 削減自体はほぼ的中 (sync_iters 121→61 で ~-1.2〜-1.5% 相当) だが、残り約 -23〜24% は最終 splat 数 -35% による rasterize コスト減の副作用
Impact
refine tick 頻度を [refine] every 100→200 に変更する hyperparameter 実験 (非 bit-exact、学習 dynamics が変わる)。12.5k Lego n=3 交互 A/B (off=every100 baseline / on=every200 candidate、他の refine パラメータ (stop_iter 等) は不変) で、PSNR guard (n=3 で on mean >= off mean - 0.05dB) と最終 splat 数の変化を主眼に計測した。PSNR Δ -0.421dB で guard を約 8.4 倍超過 — 不採用。wallclock は Δ -25.6% と大幅短縮したが、tick drain 半減 (sync_iters 121→61、期待帯 -1.2〜-1.5% 相当) は寄与のごく一部に過ぎず、大半は densification 予算削減による最終 splat 数 -35% (407.7k→266.7k) の副作用。refine.every は tick drain の独立ノブではなく、growth/clone/split イベント頻度と opacity decay 累積量 (`opacity_decay_rate` は refine cadence で適用) を同時に変える hyperparameter であることが判明した。r2 pair は事前登録条件 (load spike AND 3σ超) を満たし 1 回 retry したが、PSNR guard の判定には影響しない (PSNR は決定論的、load は wallclock のみに影響)。default config の書き換えはしない — 採否はユーザー判断待ち。
01

本文

§6 施策 #5 は「refine tick の CPU compact drain (12.5k で 3.0〜3.8s、2.4〜3.1%) は tick 頻度に比例するはずだから、頻度を半減すれば drain も半減し wallclock -1.2〜-1.5% を稼げる」という比較的単純な仮説だった。実測は wallclock こそ大幅に短縮したが、それは狙った効果ではなく、refine.every が tick drain と同時に densification 予算・opacity decay 累積量も支配する hyperparameter だったための副作用であり、PSNR guard を大幅に割り込んだ。不採用、default は変更しない。

1. gate 結果 — 6 run (メイン series、r2 pair は retry 後の値で置換)

armruntrain elapsed (s)PSNR (dB)final splatsrun 窓内 load1
offoff-r1125.2436.1314076641.11〜3.87 (正常)
onon-r193.5835.7702664741.83〜5.90 (正常〜やや上昇、判定閾値 6.0 未満)
offoff-r2129.6536.241408855**汚染: 2.4〜6.08 spike。retry (132.69s) はさらに悪化 (12.78〜14.33 spike) — 2 回とも汚染、protocol 上 retry は 1 回のみのため元値を採用**
onon-r293.7235.736267959**retry 値を採用 (2.18〜3.44、clean)。原系列 (114.02s) は load1 最大 16.97 の重度汚染で棄却**
offoff-r3124.6036.0984079201.09〜3.43 (正常)
onon-r394.8935.7012655400.68〜1.13 (正常)
**mean (n=3)****off****126.497** (σ 2.75, 2.17%)**36.157****408146**off-r2 は load 汚染 2 回とも解消できず、wallclock は参考値扱い
**mean (n=3)****on****94.063** (σ 0.72, 0.76%)**35.736****266658**on-r2 は retry で clean 値に置換済み
**Δ**—**-25.6%****-0.421****-34.67%**—

2. PSNR guard — 大幅超過、3/3 pair 一貫

pairoff PSNRon PSNRΔ
r136.13135.770-0.361
r2 (元値のみ、off/on とも original)36.24135.697-0.544
r2 (on のみ retry 値に置換、off は元値のまま — 本 finding 採用値)36.24135.736-0.505
r336.09835.701-0.397
**mean (全て元値)****36.157****35.723****-0.434**
**mean (r2-on を retry 値に置換、本 finding 採用値)****36.157****35.736****-0.421**
判定 — 不採用 (PSNR guard 不通過)

guard は「n=3 で on mean >= off mean - 0.05dB」。実測 Δ は -0.421dB で guard を約 8.4 倍超過し、3 pair 全てで一貫して on < off (-0.36〜-0.55dB) — 単一 run のノイズではなく系統的な劣化。同一 config (off-r2) を 2 回実行した際の PSNR 差 (36.241 → 36.137、Δ0.104dB) が本実装のノイズフロアの目安になるが、観測された on/off 差はその 3.5〜5 倍あり、ノイズでは説明できない。wallclock の大幅な短縮 (Δ -25.6%) はこの PSNR 劣化とセットの副作用であり、単独の高速化施策として採用することはできない。

3. なぜ wallclock が期待の 17 倍も縮んだか — drain 削減は的中、支配項は densification 予算

tick drain のメカニズムは的中
各 run の `sync_iters` (train_loop の refine tick 回数) は off=121、on=61 — ちょうど 100→200 の tick 半減が反映されている。p3-c2 の見積り (3.0〜3.8s / 121 tick ≈ 25〜31ms/tick) から、60 tick 減で ~1.5〜1.9s の節約 = 126.5s に対し約 -1.2〜-1.5% — <strong>§6 施策 #5 が予測した帯にちょうど収まる</strong>
残り約 -24% は別のメカニズム
ms/iter が off ≈11.8〜12.1 → on ≈9.1〜10.2 に低下している。これは refine tick 頻度そのものではなく、<strong>on arm の最終 splat 数が off の約 65% しかない</strong>ことによる rasterize/backward コスト減 — splat 数が少なければ 1 iter あたりのラスタライズ・勾配計算コストがほぼ線形に下がる
splat 数が減った理由 (1): densification 予算 (tick 回数律速)
`refine()` (`splat-train-v1/src/refine.rs:280`) は呼ばれるたびに 1 回、`mean_xy_grad = grad_xy_accum[i] / visible_count[i]` (`refine.rs:332`) を `grad_threshold` と比較して clone/split を判定し (`refine.rs:347-352`)、`n_new = n_kept + n_respawned + 2*n_cloned + 2*n_split` (`refine.rs:404`) で純増分を計算する。`grad_xy_accum`/`visible_count` は tick 間で正規化された平均勾配であり interval 長そのものには (定常分布なら) 依存しないため、1 tick あたりの growth 判定基準自体は every=100/200 で大きくは変わらないはずだが、<strong>growth の「発火機会」は tick 単位 (12500 iter で 121 回 vs 61 回) なので、機会が半減すれば累積 growth イベント数もほぼ半減する</strong> — 観測された splat 数 -35% はこの機会減少と整合的 (厳密な比例ではない差は学習後半の分布変化・opacity decay との相互作用によるものと推定、未検証)
splat 数が減った理由 (2): opacity decay の累積量
config.rs:196-200 のコメント通り `opacity_decay_rate` (brush 互換 sigmoid-space decay) は「refine cadence (= every iter) で適用」される。tick 頻度を半減すると、12500 iter を通じて適用される decay の累積回数も半減し、opacity 分布・prune の閾値通過タイミングが変わる — これも splat 数と学習 dynamics に効く
結論
<code>refine.every</code> は tick drain 専用の独立ノブではなく、tick cadence に紐づく複数の学習 dynamics (growth 予算・opacity decay 累積) を同時に動かす hyperparameter。§6 施策 #5 の「drain だけを削る」という前提は成立せず、実測は drain 削減効果 (的中) と densification 予算削減の副作用 (未想定、支配的) の合成値になっている

4. r2 pair の load 汚染と retry — 事前登録条件・結果・判断

汚染の発見
series 全体の独立 load sampler (`re200-12500-loadsamples.log`) を run 窓 (`.meta` の pre_date/post_date) で切り出したところ、on-r2 の窓内で load1 が 16.97 まで急騰 (baseline 0.8〜2 の 8〜17 倍)。off-r2 の窓内でも 6.08 まで一時的に上昇 (LOAD_LIMIT=6.0 相当、他 run は 1.1〜3.9 圏)
3σ 超過の確認
on 系列の clean 2 値 (on-r1=93.58, on-r3=94.89、平均 94.235、population σ≈0.655) を基準にすると on-r2 (114.02) は約 30σ の外れ値。off 系列も同様の leave-two-out で off-r2 (129.65) は off-r1/r3 (平均 124.92、σ≈0.32) から約 15σ 外れる — ただし p3-refine-3pass §7 が指摘する「自 arm 内 2 点だけの leave-one-out σ は自己言及的で脆い」罠を踏まないよう、判定の主根拠は load evidence (16.97 という桁違いの spike) に置き、3σ 超過は補強情報として扱った
retry 実施
汚染 pair (off-r2, on-r2) を事前登録条件 (load spike AND 3σ超) に基づき 1 回 retry (`splat/scripts/re200-retry-r2.sh`、gate-ab-n.sh は N 固定の全 arm 順序前提のため単一 pair retry には使えず、同じ quiesce/AC check/meta 記録プロトコルを縮約した使い捨てヘルパーとして新設)。binary sha256 は retry 時点でも series 全体を通じて `38ca2d6d...` と不変 (HEAD は他 agent の S4b merge で `1c54a0b` に進んでいたが、実行バイナリはリビルドされていない) — retry が別バイナリで走った疑いはない
retry 結果
on-r2-retry は clean (load1 2.18〜3.44) — 元値 (114.02s, PSNR 35.697) を retry 値 (93.72s, PSNR 35.736) に置換。<strong>off-r2-retry は逆に悪化 (load1 最大 14.33) — 1 回のみの retry protocol の範囲内で clean 値が得られなかった</strong>。off-r2 は元値 (129.65s) を wallclock 参考値として採用したが汚染残存を明記する
PSNR 判定への影響
PSNR は決定論的な計算経路 (loss/inference) の結果であり load (CPU 側 ambient 負荷) の影響を受けない — off-r2 の元値 (36.241) と retry 値 (36.137) の差 (0.104dB) は本実装のノイズフロア (atomic float 由来の ulp 差、既存方針で tolerance 比較) の範囲内であり、どちらを使っても on/off Δ が guard を大幅超過する結論は変わらない
ambient の推定原因
同時刻に main HEAD が `f91355e` → `1c54a0b` (別 agent の S4b ssim_reduce merge) に進んでいることを確認済み — 同機上の他 claude セッションによる cargo build/merge 作業が load spike の原因である可能性が高い。GPU 自体は本 series 専有だったため (pgrep 空確認済み)、干渉は host 側 CPU 負荷経由と推定

5. 対象外・注意点

項目内容
変更していないもの`[refine] stop_iter` / `opacity_reset_every` / `grad_threshold` / `size_threshold` / `opacity_min` / `split_scale_factor` / `reset_opacity_val` / `max_scale_cap` / `enable_mcmc_respawn` — 全て final-12500 base と同一。差分は `[refine] every` (100→200) の 1 field のみ (`splat check` で 6 config 全て deserialize/validate 済み)
default config**書き換えていない**。`splat-train-v1/src/config.rs` の `RefineConfig::default()` (`every: 100`) は本 gate では一切変更していない — 採否はユーザー判断待ち
code 変更**一切なし**。config ファイル 6 本 (`splat/configs/2026-08-30-re200-12500-{off,on}-r{1,2,3}.toml`) と retry 用の使い捨てヘルパー script (`splat/scripts/re200-retry-r2.sh`) のみを新設。`splat/crates/` 配下の src は無変更
§6 施策 #5 の再評価が必要な場合本 gate は「drain だけを削る」という前提が成立しないことを示した。候補メカニズムは (a) growth 発火機会が tick 回数 (12500 iter で 121→61 回) に直接律速される (`refine.rs:280,347-352,404`)、(b) `opacity_decay_rate` が refine cadence で累積適用される (`config.rs:196-200`) の 2 つで、本 gate はどちらが支配的かを切り分けていない。仮に再挑戦するなら、(a) 対策として growth の発火機会を iteration 数ベースに正規化する、(b) 対策として `opacity_decay_rate` を tick 頻度の逆数でスケールし累積 decay 量を固定する、の両方を独立に試し、最終 splat 数を baseline (~408k) に固定した状態で drain 削減の純効果 (期待 -1.2〜-1.5%) だけを分離計測できて初めて §6 の仮説を検証したことになる。現状の finding はその前提が崩れていることの記録であり、次の一手は別 task として起票する
03

関連 finding

© 2026 大竹律輝 — 松田研究室