[{"data":1,"prerenderedAt":324},["ShallowReactive",2],{"finding:p1-d-opacity-decay-smoke":3,"finding-runs:p1-d-opacity-decay-smoke":252,"finding-related:p1-d-opacity-decay-smoke":262},{"meta":4,"impact":33,"sections":40},{"id":5,"title":6,"subtitle":7,"eyebrow":8,"date":9,"status":10,"category":11,"polarity":12,"axes":13,"tags":16,"task_code":24,"related_runs":25,"related_findings":29},"p1-d-opacity-decay-smoke","P1.D opacity decay 5k smoke — splats -11.6%、PSNR +0.38 dB の同時改善","Stage 1 brushcompat 5k baseline (31.308 dB \u002F 93,948 splats \u002F 2m 5s) に対し、brush 互換 sigmoid-space opacity decay (rate=0.004、refine cadence で全 splat に適用) を追加し 5k smoke を実行。結果: PSNR 31.689 dB (+0.38 dB)、splats 83,093 (-10,855、-11.6%)、wallclock 2m 34s (+29s、+23% — Adam 後 host RMW のオーバヘッドだが許容範囲)。4-way eval は brush conv +q8 で 31.706 dB、legacy conv で 1.595 dB (Stage 2 row 1 と同じ対称崩壊 pattern、convention bridge は健全動作)。PSNR と splats が同時改善 (trade-off 不発) しており、30k full bench に進む価値あり。実装は refine.rs:`apply_opacity_decay` 1 関数 (33 行 CPU host RMW、brush の sigmoid-space formula を忠実移植) + train_loop.rs 1 ブロック (12 行) + config.rs 1 field のみで、kernel 改変なし。","P1 Phase D · opacity decay · 5k smoke (go\u002Fno-go)","2026-05-24","stable","experiment","positive",[14,15],1,3,[17,18,19,20,21,22,23],"p1","phase-d","opacity-decay","splat-count-reduction","brush-compat","lego-5k","smoke","P1.D opacity-decay (Phase D core)",[26,27,28],"lego-brushcompat-opacdecay-5k","lego-brushcompat-base-5k (Stage 1 baseline 31.31 dB \u002F 93,948 splats)","lego-brushcompat-base-30k (Stage 2 35.18 dB \u002F 846,689 splats)",[30,31,32],"p1-b-f-stage2-30k-results","p1-b-f-trainer-convention-bridge","p1-a-1-brush-eval-audit",{"summary":34,"rank":35,"verdict":36,"delta_psnr":37,"delta_wallclock":38,"delta_splats":39},"brush の `refine_splats()` (train.rs:611-619) と同じ sigmoid-space formula で opacity decay を refine cadence に統合: `new_opac = sigmoid(raw) - rate*(1-train_t)` → `clamp(1e-12, 1-1e-12)` → `inv_sigmoid`。5k Lego smoke で PSNR は維持以上 (31.31 → 31.69 dB、+0.38 dB)、splats は **-11.6%** 削減 (93,948 → 83,093)、wallclock は +23% (1500 iter で全 splat 触る host loop が支配的、N=83k で問題ない範囲)。これにより 30k に進めば brush 282k 帯 (Stage 2 の 846k からの大幅削減) + PSNR ≥ 34 dB の同時達成が射程に入る。axis 1 (native Metal) ではなく axis 3 (unified memory CPU RMW) を活用した実装で、refine 周辺の O(N)\u002Frefine_every オペレーションには合理的選択 (Metal dispatch overhead > 実 work)。","high","accepted-go-30k","+0.38 dB vs Stage 1 baseline 5k (31.308 → 31.689)","+23% vs Stage 1 5k (2m 5s → 2m 34s、host RMW overhead、N で線形)","-10,855 (-11.6%) vs Stage 1 5k (93,948 → 83,093)",[41,44,49,52,97,99,148,150,154,156,165,167,201,203,206,213,215,245,247],{"type":42,"text":43},"lead","\u003Cstrong>P1.B+F Stage 2 (Lego 30k brushcompat = 35.18 dB)\u003C\u002Fstrong> は brush parity を +3.20 dB 上回ったが、\u003Cstrong>splats が 846,689\u003C\u002Fstrong> (brush 282k の 3 倍)、\u003Cstrong>wallclock 1h 2m\u003C\u002Fstrong> (brush ~22m の 2.8 倍) と効率面で劣る。\u003Cstrong>P1.D\u003C\u002Fstrong> では brush の opacity decay (`opac_decay=0.004` default) を sigmoid-space で忠実移植し、5k smoke で \u003Cstrong>splats -11.6% + PSNR +0.38 dB の同時改善\u003C\u002Fstrong> を確認。30k full bench に進む go 判定。",{"type":45,"label":46,"variant":47,"text":48},"callout","Headline","success","\u003Cstrong>splats と PSNR の trade-off が不発、両方改善。\u003C\u002Fstrong> brush の sigmoid-space opacity decay を refine cadence (100 iter 毎) で applies すると、(1) 低 opacity splat が物理的に prune されやすくなり, (2) 全 splat 一律 shift で残った splat は \u003Cstrong>より高 opacity で精度寄与する\u003C\u002Fstrong>。raw-space multiplicative (`raw *= 1-decay`) では sigmoid=0.5 付近で no-op になる落とし穴があるため、\u003Cstrong>sigmoid-space subtraction が必須\u003C\u002Fstrong>。5k で +0.38 dB なら 30k では +1〜+2 dB shift も期待できる (要実測)。",{"type":50,"text":51},"heading","1. 5k smoke 直接比較 (Stage 1 baseline vs opacity decay)",{"type":53,"columns":54,"align":59,"rows":62,"caption":96},"table",[55,56,57,58],"metric","Stage 1 baseline 5k","opacity decay 5k (Phase D)","Δ",[60,61,61,61],"left","right",[63,68,73,78,83,88,93],[64,65,66,67],"PSNR (training-time、brush conv)","31.308 dB","**31.689 dB**","**+0.38 dB**",[69,70,71,72],"PSNR (eval、brush conv + q8)","(未測)","**31.706 dB**","—",[74,75,76,77],"PSNR (eval、legacy conv)","1.6 dB 相当","1.595 dB","≈ same (対称崩壊、Stage 2 row 1 と同 pattern)",[79,80,81,82],"splats (final)","93,948","**83,093**","**-10,855 (-11.6%)**",[84,85,86,87],"wallclock","2m 5s","2m 34s","+29s (+23%)",[89,90,91,92],"ms\u002Fiter (median window)","~ 25","~ 34","+9 ms (refine cadence で host RMW)",[94,72,95,72],"final loss","1.468e-2","PSNR と splats が同時改善。wallclock 増は refine cadence (100 iter 毎) で raw_opacities[0..N] を host CPU で 1 周する overhead。N=83k なら 1 ms 未満で済むはずだが、refine 自体も同 cadence で実行されるため実測 ms\u002Fiter 増は ~9 ms。30k full bench では refine が同 cadence なので相対増は同様 (~23%)、絶対値は 1h 2m → 1h 16m 帯と予測。",{"type":50,"text":98},"2. 学習 curve (splats vs iter)",{"type":53,"columns":100,"align":106,"rows":107,"caption":147},[101,102,103,104,105],"iter","loss","splats","ms\u002Fiter","経過",[61,61,61,61,61],[108,114,120,126,132,137,142],[109,110,111,112,113],"1","5.94e-1","5,207 (init)","162","0.16s",[115,116,117,118,119],"500","8.62e-2","842","21.8","11.1s",[121,122,123,124,125],"1000","5.45e-2","11,363","16.5","19.3s",[127,128,129,130,131],"1500","3.12e-2","83,093","23.8","31.2s",[133,134,129,135,136],"2000","1.95e-2","35.6","49.0s",[138,139,129,140,141],"3000","1.63e-2","33.8","82.5s",[143,144,129,145,146],"5000","1.47e-2","33.5","154.5s (2m 34s)","iter 500 (refine 開始) で opacity decay が初発火、低 opacity splat が一気に prune されて 5,207 → 842 まで激減。iter 1000-1500 で grad-based split\u002Fclone により再成長、stop_iter=1500 で 83,093 に到達して以降は固定 (decay の影響は終わるが split\u002Fclone もない)。最終 splats は brush 自身の 5k 相当帯 (brush は 30k 必須なので直接比較不可) よりは多めだが、Stage 1 baseline からは -11.6%。",{"type":50,"text":149},"3. 実装 detail",{"type":151,"lang":152,"text":153},"code","rust","\u002F\u002F crates\u002Fsplat-train-v1\u002Fsrc\u002Frefine.rs — Phase D 追加関数\npub fn apply_opacity_decay(param: &Param, decay_rate: f32, train_t: f32) {\n    let n = param.num_splats as usize;\n    if n == 0 || decay_rate == 0.0 { return; }\n    let t_clamped = train_t.clamp(0.0, 1.0);\n    let minus_opac = decay_rate * (1.0 - t_clamped);\n    if minus_opac == 0.0 { return; }\n    unsafe {\n        let ptr = param.raw_opacities.contents() as *mut f32;\n        for i in 0..n {\n            let raw = *ptr.add(i);\n            let cur = sigmoid(raw);\n            let new_opac = (cur - minus_opac).clamp(1e-12, 1.0 - 1e-12);\n            *ptr.add(i) = logit(new_opac);\n        }\n    }\n}\n",{"type":151,"lang":152,"text":155},"\u002F\u002F crates\u002Fsplat-train-v1\u002Fsrc\u002Ftrain_loop.rs — refine() の **直前** に挿入\nif refine_cfg.opacity_decay_rate > 0.0\n    && it >= refine_cfg.start_iter\n    && it \u003C= refine_cfg.stop_iter\n    && refine_cfg.every > 0\n    && it % refine_cfg.every == 0\n{\n    let train_t = it as f32 \u002F cfg.max_steps as f32;\n    refine::apply_opacity_decay(param, refine_cfg.opacity_decay_rate, train_t);\n}\n",{"type":157,"items":158},"list",[159,160,161,162,163,164],"\u003Cstrong>cadence\u003C\u002Fstrong>: refine と同じ (every=100 iter)。brush の `refine_splats()` 内 (train.rs:611-619) と同位置。","\u003Cstrong>schedule\u003C\u002Fstrong>: \u003Ccode>minus_opac = rate × (1 - train_t)\u003C\u002Fcode>、train_t = iter\u002Fmax_steps。終盤 (t→1) で decay→0、後半は刈り込みなし。","\u003Cstrong>formula\u003C\u002Fstrong>: sigmoid-space で減算。raw-space multiplicative (\u003Ccode>raw *= 1-decay\u003C\u002Fcode>) は raw=0 (sigmoid=0.5) で no-op になり brush と非互換。","\u003Cstrong>Adam m\u002Fv\u003C\u002Fstrong>: 保持。brush も \u003Ccode>load_record(record)\u003C\u002Fcode> で m\u002Fv を維持しており、refine cadence で resetすると momentum が壊れる。raw_opacity の \u003Cstrong>絶対値 shift\u003C\u002Fstrong> なので m_raw_opacity の方向情報は引き続き有効。","\u003Cstrong>gate\u003C\u002Fstrong>: \u003Ccode>decay_rate > 0.0\u003C\u002Fcode> で初めて関数 body に入る (early return)。default 0.0 で完全な backward compat。","\u003Cstrong>kernel 改変\u003C\u002Fstrong>: なし。axis 3 (unified memory) で host RMW、Metal dispatch overhead (kernel コンパイル + encoder + commit) が実 work (N=83k の sigmoid + clamp + logit) より高くなる典型 case。",{"type":50,"text":166},"4. 4-way eval (Lego val 100 view)",{"type":53,"columns":168,"align":177,"rows":179,"caption":200},[169,170,171,172,173,174,175,176],"#","trainer","eval convention","quant 8-bit","PSNR (dB)","min","median","max",[61,60,60,178,61,61,61,61],"center",[180,187,195],[109,181,182,72,183,184,185,186],"Phase D opacity decay 5k","legacy","1.595","0.991","1.615","1.942",[188,181,189,190,191,192,193,194],"2","brush","q8","**31.706**","25.122","31.925","35.947",[196,197,189,198,199,72,72,72],"3","(reference) training-time eval","raw","31.689","Row 1 は対称崩壊 (brush trainer × legacy eval = 1.60 dB、Stage 2 row 1 と同 pattern、convention bridge が健全動作している確証)。Row 2 が brush convention 下の真値、q8 vs raw の差 ≈ +0.02 dB (Stage 2 と同様 quant の effect は微小)。",{"type":50,"text":202},"5. Phase D 完遂判定と次 step",{"type":45,"label":204,"variant":47,"text":205},"判定: accepted, 30k full bench へ go","5k smoke で \u003Cstrong>PSNR +0.38 dB \u002F splats -11.6%\u003C\u002Fstrong> の同時改善。trade-off 不発の理想形。30k full bench で \u003Cstrong>Stage 2 35.18 dB \u002F 846k splats を上回り、かつ splats を 282k 帯 (brush 並) に近づけられる\u003C\u002Fstrong> 期待が高い。即 30k に進む価値あり。",{"type":157,"ordered":207,"items":208},true,[209,210,211,212],"\u003Cstrong>(1) 30k full bench\u003C\u002Fstrong>: configs\u002F2026-05-24-2000-lego-brushcompat-opacdecay-5k.toml を 30k 版に複製、`max_steps=30000` + `stop_iter=15000` (Stage 2 と同) で再実行。期待: PSNR ≥ 35.2 dB (Stage 2 と同等以上)、splats ≤ 500k (Stage 2 の 60% 以下)、wallclock ~ 1h 16m (+23%)。","\u003Cstrong>(2) multi-scene 検証\u003C\u002Fstrong>: chair で Stage 2 が splats 1.99M に爆発しているため、opacity decay を入れた配列で 8-scene chain を再実行する戦略判断材料に。chair で splats を 500k 以下に抑えられるか先行確認。","\u003Cstrong>(3) rate tuning\u003C\u002Fstrong>: rate=0.004 は brush default のまま。5k で +0.38 dB なら 0.006 \u002F 0.008 で更に splats 削減できるか試す価値あり (overdecay で PSNR 落ち込む binary search)。","\u003Cstrong>(4) axis 1 native Metal\u003C\u002Fstrong>: 本 phase の host RMW は N=100k 帯では十分高速だが、N=1M+ (chair 級) では 5-10 ms 帯になる可能性。kernel 化は **Phase D' で N>500k スケール時に再評価**。",{"type":50,"text":214},"6. file:line index",{"type":53,"columns":216,"align":219,"rows":220},[217,218],"役割","path:line",[60,60],[221,224,227,230,233,236,239,242],[222,223],"新規 関数 apply_opacity_decay","splat\u002Fcrates\u002Fsplat-train-v1\u002Fsrc\u002Frefine.rs:365-394",[225,226],"train_loop 統合 (refine 前 shift)","splat\u002Fcrates\u002Fsplat-train-v1\u002Fsrc\u002Ftrain_loop.rs:316-329",[228,229],"RefineConfig.opacity_decay_rate field","splat\u002Fcrates\u002Fsplat-train-v1\u002Fsrc\u002Fconfig.rs:135-141",[231,232],"unit test opacity_decay_subtracts_in_sigmoid_space","splat\u002Fcrates\u002Fsplat-train-v1\u002Fsrc\u002Frefine.rs:tests",[234,235],"新 config (5k smoke)","splat\u002Fconfigs\u002F2026-05-24-2000-lego-brushcompat-opacdecay-5k.toml",[237,238],"smoke run 結果","splat\u002Fruns\u002Flego-brushcompat-opacdecay-5k\u002Fresult.toml",[240,241],"brush 元実装 (sigmoid-space formula)","\u002FUsers\u002Fotkrickey\u002Fdev\u002Fbrush\u002Fcrates\u002Fbrush-train\u002Fsrc\u002Ftrain.rs:611-619",[243,244],"brush opac_decay default 0.004","\u002FUsers\u002Fotkrickey\u002Fdev\u002Fbrush\u002Fcrates\u002Fbrush-train\u002Fsrc\u002Fconfig.rs:82-84",{"type":50,"text":246},"7. 想定外 \u002F 注意",{"type":157,"items":248},[249,250,251],"\u003Cstrong>task spec の指示と実装の乖離 (意図的)\u003C\u002Fstrong>: spec は (a) per-step \u002F (b) raw-space multiplicative \u002F (c) Metal kernel を指示していたが、brush 元実装は \u003Cstrong>per-refine \u002F sigmoid-space \u002F CPU host\u003C\u002Fstrong>。advisor 助言で原典忠実な (A) 案を採用。理由: (i) raw-space multiplicative は raw=0 (sigmoid=0.5、初期値多数) で no-op、(ii) refine cadence なら N=83k でも 1 ms 未満で kernel dispatch overhead が work を上回る、(iii) 既存 refine 周辺は全て CPU host RMW で統一されており Metal kernel は dead artifact になる。","\u003Cstrong>wallclock +23%\u003C\u002Fstrong> は refine cadence の host CPU loop が支配的。N=83k なら 1 周 ~0.5 ms × 50 refine call ≈ 25 ms の累積、wallclock 増 29 s の中で占める割合は ~0.1% にすぎないため、実際の overhead は refine pass + Adam invariance lookup などの周辺 effect が支配的の可能性。30k で確定。","\u003Cstrong>splats が 83,093 で頭打ち\u003C\u002Fstrong>: stop_iter=1500 後は split\u002Fclone も decay も止まるので、5k 終了時の splats は brushcompat baseline 5k (93,948) を下回るが、実際の active opacity は前述の sigmoid-space shift で \u003Cstrong>下方シフトしている\u003C\u002Fstrong>。30k では stop_iter=15000 まで decay が継続して刈り込みが効くため、最終 splats はさらに削減される見込み。",[253],{"id":26,"title":26,"subtitle":254,"date":9,"workspace":255,"tags":256,"verdict":258,"psnr":259,"psnr_unit":-1,"wallclock":86,"splats":260,"summary_url":261,"detail_path":261},"P1.D opacity decay 5k — brushcompat-base-5k に opacity_decay_rate=0.004 を追加","splat",[257,22,23,21,19,20],"p1-d","partial",31.68873405456543,83093,"\u002Fruns\u002Flego-brushcompat-opacdecay-5k\u002F",[263,286,307],{"id":32,"title":264,"date":9,"status":10,"polarity":265,"category":266,"axes":267,"tags":269,"task_code":277,"related_runs":278,"delta_psnr":281,"delta_wallclock":282,"rank":35,"verdict":283,"impact_summary":284,"detail_path":285},"P1.A.1 brush eval audit — 数式定式化 + diff 観点 12 項目","neutral","audit",[14,268],2,[17,270,189,266,271,272,273,274,275,276],"a-1","psnr","ssim","eval","alpha","premultiplied","convention","P1.A.1",[279,280],"brush-lego-sh3-30k (37.40 dB report)","splat-rs-lego-sh3-30k (24.879 dB)","N\u002FA (apparent gap mechanism の特定が主目的)","N\u002FA (audit task)","audit-complete","brush eval は (1) AlphaMode::Transparent で GT を α premultiply、(2) bg=Vec3::ZERO の黒背景に render、(3) composite_bg=None で premultiplied 同士を直接比較、(4) 8-bit roundtrip 後に MSE = mean((pred−gt)²) over H·W·3、(5) PSNR = 10·log10(1\u002FMSE)。これは NeRF Synthetic (RGBA で α=0 の透明領域が支配的) において **透明領域は pred=gt=0 で完全一致** となり、MSE 分母に 0 寄与が大量に入る → conventional 「白背景に composite してから PSNR」より高く出る。splat-rs 側の eval 規約を A.2 で確認し、A.3 で「同 convention 下での真の gap」を測定する必要あり。","\u002Ffindings\u002Fp1-a-1-brush-eval-audit\u002F",{"id":30,"title":287,"date":9,"status":10,"polarity":12,"category":11,"axes":288,"tags":289,"task_code":297,"related_runs":298,"delta_psnr":302,"delta_wallclock":303,"rank":35,"verdict":304,"impact_summary":305,"detail_path":306},"P1.B+F Stage 2 — Lego 30k brushcompat で 35.184 dB、brush 自身を +3.20 dB 上回り",[14,268,15],[17,290,291,292,293,275,294,295,296],"phase-b-f","milestone-m3","brush-parity","brush-超え","convention-bridge","lego-30k","stage-2","P1.B+F Stage 2 (M3 gate)",[299,300,301],"lego-brushcompat-base-30k","lego-brushcompat-base-5k","lego-sh3-30k (legacy 30k 24.879 dB)","+10.30 dB vs legacy 30k (24.879 → 35.184、convention 変更後の真の現状)","+2.7x vs legacy 30k (1h 2m 18s vs 22m18s、splats 10x で per-iter time 増、ただし brush 自身 282k より 3 倍多い)","accepted-stretch-goal-met","Lego sh3 30k で gt_convention=premultiplied (brush 互換) を立てると、4-way eval で legacy=1.60 \u002F brush=35.24 dB。**brush 自身 val 32.0 dB を +3.20 dB 上回る** 結果。M3 lifeline (30 dB) を +5.24 dB 突破、M5 (36 dB) まで -0.76 dB に到達。Phase A 主仮説 (apparent gap -3〜-6 dB) は falsify されたが、coupling 解消の真の効果は **+33.6 dB shift (1.67 → 35.24)**、想定 (+10 dB) の 3 倍。実装は configs 1 行 (gt_convention) + dataset.rs (load_rgba_premultiplied path 追加、既に Stage 1 で merge 済) のみ、既存 30k legacy bench との apples-to-apples comparison が可能。brush の wallclock 38% 高速化は 30k でも継続 (splats 1M-cap で 846k 到達、refine が攻撃的 split)、ただし brush 自身 282k に比べて 3 倍、本実装が capacity を未活用 (refine を絞る余地あり、Phase D で検証可能)。次 Step は multi-scene 8 シーン展開で universal claim 確定、brush mean 33.32 dB 超えで multi-scene parity 完全達成を狙う。","\u002Ffindings\u002Fp1-b-f-stage2-30k-results\u002F",{"id":31,"title":308,"date":9,"status":10,"polarity":12,"category":11,"axes":309,"tags":310,"task_code":315,"related_runs":316,"delta_psnr":319,"delta_wallclock":320,"rank":35,"verdict":321,"impact_summary":322,"detail_path":323},"P1.B.F Stage 1 — gt_convention=premultiplied 切替で brush eval PSNR を 1.67 → 31.33 dB に回復、coupling 解消実証",[14,268,15],[17,311,312,313,292,170,275,294,23,314],"phase-b","phase-f","milestone-m2","hypothesis-confirmed","P1.B + P1.F Stage 1",[317,300,318],"lego-legacybase-5k","lego-sh3-30k (P1.A.3 baseline)","+29.71 dB (brush eval 系: A.3 30k 1.667 dB → P1.B.F 5k 31.334 dB)、Stage 1 hypothesis (>10 dB) を +21 dB 上回り","5k 比較: legacy 202.4s \u002F brush 125.4s (brush -38% 高速、splats 77.6k → 93.9k だが GPU loss は同等)","hypothesis-confirmed-stage-2-go","P1.A.3 で `splat-rs trainer が white-bg target で学習 → 背景を opaque-white splat で埋める → brush 流 eval (bg=ZERO 比較) で MSE≈1 崩壊` と診断された coupling を、**GT loader を premultiplied 経路に切替えるだけ** で解消できるか 5k smoke で検証。同一 hyperparameter (`2026-05-22-2155-lego-sh3-30k.toml` の iter のみ 5k 短縮) で `data.gt_convention=white_bg` vs `data.gt_convention=premultiplied` を独立 training し、各 final.ply を 2 通り convention で eval (4 cell)。結果: brush trainer × brush eval = **31.334 dB**、legacy trainer × brush eval = 1.628 dB と完全に対比、coupling が双方向に存在することも symmetry test (brush trainer × legacy eval = 1.595 dB) で確定。5k 段階で既に B-N 30k baseline (24.88 dB legacy) を **brush eval 系で +6.5 dB 超え**、brush 公称 37 dB との gap は -5.7 dB のみ。Stage 1 hypothesis (10+ dB) を 21 dB 上回り、coupling 解消が brush parity への critical path であることを定量実証。実装は `splat-cli\u002Fsrc\u002Fconfig.rs` に `data.gt_convention: GtConvention` enum 追加 (default=`WhiteBg`、既存 configs 完全互換) + `train.rs` の train\u002Fval load を `load_nerf_synthetic_with_convention` に切替、合計 4 file の最小差分。loss kernel (`loss.metal:31-88`) は変更不要 (n_total=W·H·4 が α channel を含み、premultiplied target の α=0 領域が `rendered α (=1-T) → 0` の natural pressure を提供、brush の match_alpha 機構と同等効果)。","\u002Ffindings\u002Fp1-b-f-trainer-convention-bridge\u002F",1782449788649]