SGLD gate を paper 流に書き換えたら mean_noise_weight が ~50x ズレ、calibration を直しても iter 240/500 で SIGSEGV。Bisect smoke で真の root cause は『L1 regularizer が opacity を 0 まで shrink → refine prune が全 splat を削除 → 空 buffer で SIGSEGV』と判明。
A.2 part 1 で apply_mcmc_noise の SGLD gate を paper 流 σ(-k(o-t)) に書き換えたところ、mean_noise_weight = 5e5 が旧 gate 前提のチューニング値だったため有効 noise が ~50-150 倍に膨張し loss 発散 → kernel segfault。Calibration を 1/100 にしても iter ~240 で同じ SIGSEGV。Bisect smoke 4 件で真の root cause は L1 regularizer (opacity_l1 + scale_eigen_l1) が opacity を 0 まで shrink → refine prune が全 splat を削除 → 空 buffer で次 forward kernel が SIGSEGV、というアルゴリズム順序問題と判明した。
mean_noise_weight = 5e5) を 30k full で実行 → iter 1 直後に Metal kernel が segfault (loss 発散 → NaN → kernel crash と推定)mean_noise_weight=5e5 のみ (scale_l1 / opacity_l1 OFF) で 100 iter 完走、loss は ~0.21 で安定 (sh=0)scale_eigen_l1=0.01 のみ: 100 iter 完走、loss は ~0.28 で安定 (sh=0)A.2 part 1 で apply_mcmc_noise の gate factor を以下に変更した。
// 旧 (brush 流近似): low-opacity 完全 reject
let factor = (1.0 - sigmoid(raw_opac)).powi(150);
// 新 (paper SGLD): low-opacity full noise
let factor = sigmoid(-cfg.noise_gate_k * (opac - cfg.noise_gate_t));
// k=100, t=0.995 (paper default)
両式の振る舞いを o (opacity = sigmoid(raw_opac)) で比較する。
| o | 旧 (1-σ)^150 | 新 σ(-100·(o-0.995)) |
|---|---|---|
| 0.0 | 1.0 | 1.0 |
| 0.1 | ~5e-50 | 0.9999 |
| 0.5 | ~7e-46 | 0.9999 |
| 0.9 | ~1e-150 | 0.9999 |
| 0.995 | ~6e-345 | 0.5 |
| 0.999 | ~0 | 0.0179 |
mean_noise_weight = 5e5 は旧式 (実質ゼロ gate) 前提でチューニングされた値。新式 (フル gate) に切り替えると、noise の実効大きさが ~50-150 倍 に。Adam step (lr_means = 1.6e-4) の数千倍の random walk が means に注入され、image gradient signal が埋もれて loss が発散する。
新式での per-iter noise (Lego 5207 splats, low-opacity 80%):
coeff = lr_mean * median_scale * mean_noise_weight
= 1.6e-4 * 0.01 * 5e5 = 0.8
factor ≈ 1.0 (低 opacity splat)
sample ~ N(0, 1)
noise_per_axis = sample * 0.8, clamp(-median_scale, median_scale) = ±0.01
vs Adam step on means ≈ 1.6e-4 * |grad| ≈ 1.6e-6
ratio: noise / Adam = 0.01 / 1.6e-6 ≈ 6000x
純粋ランダムウォークが image-loss 由来の最適化を覆い隠す状態。
5e5 → 5e3 〜 1e4 に。新 gate で「旧式と同程度の有効 noise」を目指す。config 修正のみ、コード変更不要。次回 verify run で mean_noise_weight = 5e3 から始め、PSNR 25 dB レンジに収束するかで判断。
paper Eq.: ε = λ_lr · σ(-k(o-t)) · Σ · η。ここで λ_lr は Adam lr (1.6e-4)、Σ は covariance matrix (3x3、log_scales + rot_quats から再構成)、η は N(0, I)。現状実装は Σ を median_scale スカラーで近似 (rotation 不変の axis-aligned 単一値)。これを正確な Σ · η に置換 (rotation 回せば論文同等)。mean_noise_weight はそもそも paper 式には登場しないので、5e5 は別由来 (brush の流儀)。工数: 半日 (rotation rebuild + Σ · η 計算が apply_mcmc_noise に必要)。
iter < 500 (= relocation_start) の間は noise も OFF。paper §3.2 は relocation のみ warmup 記述だが、本実装で必要なら拡張。工数: 1h (gate を train_loop 側で挟む)。
lego-mcmc-30k config を 手付けせず、本書に「次回検証時の手順」として残すdocs/findings/mcmc-3-defects.md の A.2 完了基準を「ハイパパラ calibration が次セッション残課題」と注記lego-mcmc-30k.toml の mean_noise_weight を 5.0e5 → 5.0e3 にmean_noise_weight = 5.0e3 (50x 補正) でも iter 1 直後に同じ segfault。
短期 smoke で各成分を bisect 実行 (splat/configs/2026-05-23-01XX-mcmc-*.toml)。
| smoke | components | 300-500 iter 結果 |
|---|---|---|
| mcmc-l1-only-smoke (300 iter) | scale_l1=0.01 + opacity_l1=0.01、noise=0、relocation=OFF | 完走、PSNR 1.08 dB、loss 0.34→0.91 発散 |
| mcmc-noise-sh3-smoke (300 iter) | noise=5e3、L1=0、relocation=OFF | 完走、PSNR 12.90 dB、loss 0.34→0.12 収束 |
| mcmc-combo-iter-bisect (20 iter) | noise=5e3 + L1=0.01、relocation=OFF | 完走、PSNR 5.19 dB |
| mcmc-combo-500 (500 iter) | 同上、500 iter | iter ~240 で SIGSEGV (exit=139) |
| mcmc-l1-500 (500 iter) | scale_l1 + opacity_l1 のみ、noise=0、relocation=OFF | iter 500 で splats 0 → eval が SIGSEGV (exit=139) |
L1 regularizer (opacity_l1 + scale_eigen_l1) が opacity を 0 まで shrink → refine prune が全 splat を削除 → 空 buffer で次の forward kernel が SIGSEGV。
iter 500 / 500 loss 9.0672e-1 splats 0 12.6 ms/iter
training done in 6.82s
final splats: 0
-- evaluation (val) --
[SIGSEGV]
paper の MCMC 設計では (1) L1 が opacity shrink を強制 → (2) relocation tick (default iter 500 以降毎 100 iter) が dead splat を live splat の clone で復活 → (3) 結果として live 数は維持される、という流れ。本実装の場合、relocation が refine prune の prune slot に依存しており (enable_mcmc_respawn=true だった旧経路)、relocation を独立 tick として組み込んだ新経路 (A.2 part 2) では「relocation tick の前に refine prune が全 splat を消す」順序問題が出る。
opacity_threshold = 0.005 を 0.001 等にし、refine が prune する前に MCMC relocation が処理できる順序にするenable_mcmc_respawn=true の旧経路を MCMC 有効時のみ default に: refine prune と relocation を同一 tick で密結合する旧設計のほうが安定するかもpaper の精神は「shrink + 再配置で active 数を保つ」が、本実装の prune が 全滅させてしまう点が真の bug。calibration ではなくアルゴリズム順序の問題。
mcmc-3-defects.mdsplat/configs/2026-05-22-2305-lego-mcmc-30k.toml2026-05-22-2355-mcmc-noise-only-smoke.toml, 2026-05-22-2356-mcmc-scale-l1-smoke.toml, 2026-05-22-2357-mcmc-all-smoke.tomlこの finding が観測された / 言及している実験 run。