Unified geo-tex DiT — why the 60K checkpoint underperforms
full audit vs the released TRELLIS.2-4B · 2026-08-17
3d-genauditevalgeotex
TL;DR
The run did not train the config we thought it did.
The launch script was edited 26 hours into a 30.5-hour run that never restarted.
Six hyperparameters differ from what we believed was running.
Root cause found: our geometry stream barely reads the input image.
Swap in a completely unrelated photo and the geometry output changes by 3%.
For TRELLIS.2 the same swap changes it by 40%. Our geometry is mostly
reproducing the ground-truth voxel occupancy we hand it for free.
Every headline metric we started with was misleading.
Latent MSE ranked the visually-worst asset as the best of 16. Render PSNR said we
beat TRELLIS.2 — because our voxels are GT-aligned by construction.
Not a wiring bug. Seven specific bug hypotheses were tested and
ruled out with measurements. One structural suspect (RoPE applied one-sidedly
between voxels and conditioning) remains untested and needs an A/B run.
1 · The run trained the wrong configuration
Training started 2026-08-15 23:09, ran 30h30m with zero restarts, ended 2026-08-17 05:39.
train_native_geotex.sh was edited at 2026-08-17 01:30 — 26 hours in.
The launcher reads the script once, at launch. None of the edits applied.
Read back from the checkpoint itself (config.json + weight shapes + training_args.bin + log),
not from the script:
parameter
what actually ran
what we believed
mlp_ratio
4.0
5.3334 (the released value)
p_corner
0.4
0.2
p_lag
0.0 — the lag regime was never trained
0.4
dino_drop_prob
0.3 — only 63% of samples had full conditioning
0.0
EMA decay
0.998 (~500-step window)
0.9999
LR schedule
linear decay to 0
constant with warmup
Two conclusions we had already drawn were wrong because of this:
"EMA gives nothing" (the window was 500 steps, not 10k — 0.9999 has still never been tested)
and "the config has saturated at 60K" (the LR was driven to zero; a flat tail was guaranteed).
Fix now in place: the cluster skill requires reading hyperparameters back out of the
first checkpoint — config.json, training_args.bin, weight shapes and the log — because
structural values such as mlp_ratio are not stored in config.json at all.
2 · Every metric we started with was lying
A baseball is a smooth sphere whose stitching is raised geometry, not paint.
The bulk of its voxels are trivially predictable; the seams carry all the visual weight
but occupy a tiny fraction of the voxels.
So an average over all voxels scores it as the easiest asset in the set.
Latent MSE is diluted by easy voxels: the asset it ranks best is the one that looks worst.
2b · The other three failures
metric
how it lied
evidence
latent MSE
rewards mean collapse
on the tower we "beat" TRELLIS.2 1.294 vs 1.380 — with colour spread 77% of GT vs their 103%.
We won by not committing.
render PSNR
dominated by alignment
we "win" 23.4 vs 21.9 purely because our voxel coordinates are the ground truth's
and render pixel-aligned. TRELLIS.2 generates its own and gets punished for any shift
(its pagoda scores 15.0 while looking fine).
signed detail ratio
>100% is not better
our baseball surface carries 181% of GT high-frequency — that is noise, not detail.
Both directions are failures, so the metric must be |x/x_GT − 1|.
One common cause: any scalar averaged over all voxels or pixels is fooled by a
distribution of "mostly trivial + a few decisive".
3 · Root cause — the geometry stream barely uses the image
Test: feed the model a completely different object's photo and measure how much the
single-step prediction changes, at pure noise (t=1.0).
A model that uses its conditioning degrades a lot. A model that ignores it does not move.
Both models are given the same 32³ ground-truth voxel occupancy, so this is a like-for-like test.
Our texture stream reads the image (1.35). Our geometry stream does not (1.045). TRELLIS.2's does (1.40).
3b · Why it never had to learn
The voxel coordinates are handed to us — a 32³ occupancy grid already fixes the silhouette,
so a model can score well without ever looking at the image.
TRELLIS.2 trains under the same setup and still learns to use the image, so this is not
inherent to the task. It is specific to our training.
Counting the effective supervision for "single image → geometry":
ours
TRELLIS.2 img2shape
total steps
60,000
1,000,000 (from their released training config)
x single-image task
50% (30% multi-view, 20% text)
100%
x geometry has its own loss
60% (masked at the t=0 corner)
100%
x full conditioning
63% (dropout 0.1 plus a second DINO dropout 0.3)
90%
effective steps
11,340
900,000
~79x, and that is a lower bound — their GPU count is not published.
The extra dino_drop=0.3 is the worst of these. It creates a third training state —
"Qwen tokens present, DINO absent" on 27% of samples — and DINO's 1029 patch tokens are the
only spatially precise signal we have. The model learned a solution that does not need them.
4 · Consequence — CFG amplifies a near-zero direction
Classifier-free guidance pushes along cond − uncond.
If the geometry stream barely distinguishes the two, that direction is almost pure noise
(measured std 0.043–0.086).
We inherited TRELLIS.2's guidance_strength = 7.5 without ever validating it on our model.
Monotonic across all five settings. At 7.5 the surface carries 181% of GT high-frequency; at 1.0 it carries 73%. Texture MSE improves 19% just by turning guidance down.
5 · What the failure actually looks like
Rendered on the normal channel, which carries no texture and never touches alpha.
(The default shaded channel composites against black — a low PBR alpha renders a
watertight mesh as if it were full of holes, and that cost us a day of wrong conclusions.)
Two opposite failure modes coexist, which is why a signed metric averages them away.
Left GT, middle ours, right TRELLIS.2. Ours: ripples across the smooth ball, lattice mushed on the tower, thin slats dissolved on the bench.
6 · The cross test that isolated it
Same texture function, same image conditioning, same seed. Only the geometry changes.
Feeding it TRELLIS.2's sampled shape latent — a different model, a different voxel set,
not an encoder output — still produces crisp stitching.
So the texture stream has no train/inference distribution mismatch. The entire cascade
gap is our geometry latent.
Our texture is fine on GT geometry AND on TRELLIS.2's geometry. It only breaks on our own.
7 · Measured against TRELLIS.2, on one common ruler
MSE says geometry is 3x worse and texture only 14% worse — but MSE is exactly the metric that
rewards our texture's under-saturation.
Put both on the same deviation ruler and the two streams are equally far off.
Geometry 2.9x, texture 3.0x. The '14%' was an artifact of the metric, not a real difference between the streams.
8 · Bug hypotheses tested and ruled out
hypothesis
how it was ruled out
the geometry condition is wired wrong into the texture branch
the same function produces crisp results on GT geometry and on TRELLIS.2's geometry,
and fails only on ours — the only variable is the latent
the geometry loss weight is zero
it is 1.0 and applied: total loss 1.003 = geo 0.406 + tex 0.597
resampling the texture noise every rollout step injects a random walk
fixing the noise changed nothing: 112% vs 112% surface high-frequency, per asset
our timestep distribution differs from the official one
both are uniform for the SLAT flows (only their sparse-structure stage is logit-normal)
joint attention masks geometry away from the conditioning
one varlen flash attention over all three streams, no mask
train/inference conditioning assembly differs
identical on the single-image path (the view-embedding add is multi-image only)
we run at a different voxel resolution than the official pipeline
both 32³; their 512 pipeline uses ss_res=32 as well
the sampler parameters drifted
all 10 fields match the released pipeline.json exactly
9 · Still open
Undertrained, or structurally unable to learn? Unresolved.
Conditioning sensitivity was flat from step 52k to 60k (1.047 → 1.045) — but the LR had already
decayed to ~0 there, so that proves nothing, and SAVE_KEEP=5 had deleted every earlier
checkpoint. This determines whether more steps help at all.
RoPE is applied one-sidedly between voxels and conditioning.
Conditioning tokens sit at coordinate (0,0,0) while voxel queries are rotated by their own 3D
position, so voxel-to-conditioning attention is entangled with absolute position. TRELLIS.2 uses
cross-attention, where neither side is rotated. FLUX-style MMDiT does zero its text ids, so
the pattern exists in production — but text-to-image never needs precise patch-to-voxel
correspondence and our geometry does. Untested: the objects fill the 32³ grid, leaving no room
for the translation probe. It needs an A/B run.
Does the conditioning stream earn its 133M parameters? That is 20% of the
model. TRELLIS.2 spends essentially nothing on conditioning (K/V projections inside cross-attention).
The A/B this was supposed to win was never run.
10 · Next steps
#
action
why
cost
1
Run the aligned config for real; verify from checkpoint-2000, not from the script
§1
free
2
Log conditioning sensitivity every 2000 steps
answers §9 by step ~6000 instead of never — flat at 1.0 means structural, climbing means training length
2 forward passes
3
Keep sparse early checkpoints (2k/6k/10k/20k)
the trend was unrecoverable this round
disk
4
dino_drop 0.3 → 0
removes the state that taught the model to work without DINO
free
5
p_corner 0.4 → 0.2
recovers 20% of geometry supervision
free
6
Recalibrate CFG on the val split
inference-only, measured -19% texture MSE
20 min
7
RoPE control arm
the only way to settle §9's second item
one node
Recommendation: run a 5–10k step probe before committing another 30 hours.
The probe is not for quality numbers — it answers one falsifiable question:
with the config fixed, does conditioning sensitivity move off 1.045?
If yes, it is a training-length problem and the full run is worth it. If no, it is structural and
the RoPE arm becomes the priority instead of burning another 1,000 GPU-hours.
Calibrate expectations. We are 655M against three dedicated 1.29B
specialists, and 60k steps against 1M. Even with every fix above, matching TRELLIS.2 is not the bar.
Success for the next run is conditioning sensitivity moves and surface deviation drops from 0.261.
Appendix · measurement hygiene
n=8–16 is underpowered. Asset-to-asset variation is 31%, so n=8 gives a ±21.6% confidence
interval. Both headline comparisons here are only marginally significant on a paired test
(texture t=1.02 including a broken-GT asset, t=2.37 without it; geometry t=2.02).
Formal comparisons need n=200, which is now a pinned split.
Some ground truth is wrong. 8,101 assets have a colourful input render and a grey GT
texture, and all 8,101 are labelled "fine" because the quality filter only checks the opposite
direction. On those assets the metric penalises the correct answer — TRELLIS.2 faithfully
reproduced a golden lamp and scored 1.44 while our grey output scored 0.90.
They are now excluded from val/test.
Texture must be evaluated on ground-truth geometry. Otherwise you are measuring geometry —
that is how we first concluded, incorrectly, that our texture matched TRELLIS.2's.
Held-out sets must be carved from the smallest pool and stripped from all of them. The
previous 1% was removed only from the training list; the upstream 1.8M and 800k pools still
contained 99.9% of it, so the first pretraining run would have silently eaten the eval set.
655M three-stream MMDiT · 60,000 steps · 32xH200 · 30.5h · all numbers measured on checkpoint-60000, n=8-16 unless stated