Repository navigation
MOSS-Image: the image decoder on Metal - #1687
Conversation
7597295 to
df462b7
Compare
|
Rebased onto 20705d6 (with #1512 under it). With the case driver now shared by Metal and CUDA (#1528), the decoder is the Metal backend's Checked: fmt, lean (0 new) and |
|
Heads-up @BobClawblaw: #1512 is merged into main (1157ea5), so this one can be rebased whenever suits you. Once it's down to your two commits, we'll gate it and land it. Thanks for the encoder! |
df462b7 to
02bf950
Compare
|
Rebased onto main bd10bd9: the PR is now its two commits (522108b, the decoder, then 02bf950, its docs); the decoder's files are unchanged. Checked on the same two commits over 1157ea5 (main since adds only a CHANGELOG line): |
decoder.zig runs the checkpoint's vae/ decoder as the pipeline's decode does, in float32: the latents' batch norm undone and the patches unpacked, the mid block with its attention, the up blocks, then the pipeline's 8-bit conversion. Weights as published (float32); fp32 sums, GroupNorm statistics and attention. The 3x3 convolutions run as Winograd F(2x2, 3x3): the weights' transform in float64 at load, rounded once, the input's and output's transforms in fp32 in the kernel. tf-moss-image decodes after the transformer frees and writes picture.png. tf-moss-image-decoder gates a decode against moss_image_case.py vae's float64 one: within twice torch float32's distance (reference.within) in cosine error and worst pixel row, and repeated byte for byte, or it fails. Nine kernels in moss_image_decoder.metal, each through the contract's gates 1-5 with nine controls.
02bf950 to
55f3100
Compare
|
Thank you @BobClawblaw. This is merged into main with your commits under your own name, so you're listed as a TensorFold contributor. It ships in the next release, credited to you in the changelog and the release notes. |
|
Thanks @BobClawblaw! MOSS-Image's image decoder on Metal, with |
What this changes
Fixes #1504. MOSS-Image's image decoder on Metal, inside the
moss_imagefamily (#1457, #1512). The decoder is the checkpoint'svae/, FLUX.2's small VAE decoder (AutoencoderKLFlux2), run as the pipeline's decode does, in float32: the latents' batch norm undone with the saved statistics, the 2x2 patches unpacked,post_quant_conv,conv_in, the middle block with its single-head attention, four upsampling stages,conv_out, then the pipeline's own 8-bit conversion.tf-moss-imagenow frees the transformer and writespicture.pngitself, so torch no longer decodes.Stacked on #1512: the first three commits are #1512's. This PR's own are the last two,
602714801(code) and7597295ff(docs). Once #1512 lands, I'll rebase this onto main.decoder.zig(the releasedvae/config.jsongeometry, checked; the float32 weights as published; the forward and its scratch) andpng.zig(the picture's PNG encoder).zig/kernels/metal/moss_image_decoder.metal:md_unpack,md_winoandmd_wino_32(the 3x3 convolutions as Winograd F(2x2, 3x3)),md_conv(1x1),md_norm(GroupNorm, with or without SiLU),md_group_partandmd_group_finish(its statistics),md_attentionandmd_pixels. No shared kernel changes.tf-moss-imagedecodes the final latents after the transformer is freed and writespicture.pngbesidelatents.f32.tf-moss-image-decoder MODEL_DIR LATENTS WIDTH HEIGHT REFERENCE TORCH OUTchecks one decode. It must be within twice torch float32's distance from float64, in cosine error and in its worst row of pixels, and a second decode must repeat the first byte for byte. It exits 1 otherwise. The gate isreference.within, beside the encoder's, with a test that plants a row further off than the reference allows.vae, which writes the publisher's decoder's output before the clamp in float64 on the CPU and in float32 on MPS, as the pipeline runs it.Decoder.loadrefuses a GPU without tensor units and names it, likeModel.loadandEncoder.load.Receipt
Environment:
602714801(code) on MOSS-Image: the prompt encoder on Metal #1512's92c829887, on maindd0a9cc5e.OpenMOSS-Team/MOSS-Image-Base@85975793d8b3d432f1b6e27f352abaa4c2a4c0d3.pipeline_moss_image.py(sha256-checked) and diffusers 0.42.0'sAutoencoderKLFlux2, on torch 2.14.1.Precision: float32 throughout, as the pipeline decodes. Weights as published (float32), fp32 sums in every convolution and in the attention, GroupNorm's mean and variance in two fp32 passes, and the pipeline's own 8-bit conversion (clamp,
* 0.5 + 0.5,* 255, round half to even). No half, int8 or lossy weight.Exactness, on the transformer's final latents of the fox case (1024x1024) and the card's sticky-note case (1200x880), against the publisher's decoder in float64 on the CPU:
tf-moss-image's latents equal the pinned run's byte for byte; with--prompt, they equal MOSS-Image: the prompt encoder on Metal #1512's byte for byte. The decoded picture is unchanged across rebases.Speed, back to back in one session with no other GPU work, the median of three:
Memory: the decoder's scratch is five float32 picture tensors at the widest stage, 4.7 GB at 1024x1024, allocated after the transformer is freed.
tf-moss-imagepeaks at an 11.3 GB footprint from a case's rows, and at 16.4 GB with--prompt, while the encoder is loaded.Prompt speed: n/a, not a token family.
Kernel contract,
tf-kernel-contract --only "moss image"on the M5 Ultra:md_winoandmd_wino_32run as a plain 3x3, a 3x3 read nearest from half size, and a 3x3 with a residual.md_convruns with and without a residual,md_normwith and without SiLU, andmd_group_partplain and centered.md_pixelsis checked byte for byte against the same fp32 steps, with every tie between two bytes among its inputs.moss_image_records.zig.Tests run: all passed.
zig build test,test-golden,test-kernel-contract,test-server-security test-pull-security,fuzz-cortheon,dev-tools -Doptimize=Debugandcheck-release-paths.zig fmt --check build.zig zig.tools/zig/lean_check.py: 426 problems, the same as MOSS-Image: the prompt encoder on Metal #1512's head.Not run, and why:
Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (n/a: no drafts, prompt state or concurrency)SUMMARYline is in the receipt.