Add bf16 mixed-precision inference option (--bf16) - #164
Open
Chgara (chgara) wants to merge 1 commit into
Open
Conversation
Expose the training-time mixed precision policy (encoder in bf16, neck/heads/refiner in fp32) for inference, with the encoder weights stored in bf16 so that they are not held twice by the autocast weight cache. Roughly halves the weight memory of MoGe-2/3 (e.g. 5.0 GB -> 2.7 GB for moge-3-vitg) at the same matmul precision the models were trained with. - MoGeModel.enable_mixed_precision(dtype, cast_encoder_weights=False): optionally cast the encoder weights - MoGeModel.dtype now reads the neck, so infer() keeps the input image (and uv grid) in fp32 under mixed precision - moge infer --bf16 (v2/v3)
Author
|
@microsoft-github-policy-service agree company="Speridlabs" |
Chgara (chgara)
marked this pull request as ready for review
September 8, 2026 21:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an inference option that runs the ViT encoder in bf16 while keeping the neck, heads and (for MoGe-3) the sparse refiner in fp32 — the same fine-grained mixed precision policy
train_moge3.py --precision mixed_bf16uses — with the encoder weights stored in bf16.MoGeModel.enable_mixed_precision(dtype, cast_encoder_weights=False)(v2/v3): new flag that also casts the encoder weights todtype.MoGeModel.dtype(v2/v3) now reads the neck instead of the first parameter, soinfer()keeps the input image and the uv grid in fp32 under mixed precision (as in training) and the refiner keeps receiving fp32 features.moge infer --bf16(v2/v3 only, mutually exclusive with--fp16).Motivation
The current
--fp16path for v3 keeps fp32 weights and wraps the whole forward in fp16 autocast. Two consequences:moge-3-vitgpeaks at roughly 5.0 GB (fp32 weights) + 2.5 GB (fp16 copies) of weight memory alone. The fp32 default is 5.0 GB. This PR's option is ≈ 2.7 GB (1.14B encoder params × 2 B + 0.11B × 4 B), and there is nothing to cache because the encoder weights already are bf16.--fp16, which they never saw in training; the encoder was trained under bf16 autocast, so bf16 for the encoder and fp32 for everything else is the policy the checkpoints were trained with.Weight memory (GB = 10⁹ bytes):
--fp16(fp32 weights + autocast cache, peak).half())--bf16(this PR)Numerically the matmuls see the same bf16 weights as under plain autocast; only the few fp32-policy ops (layer norms, positional-embedding interpolation) now read bf16-rounded parameters.
Why the
dtypeproperty changeinfer()doesimage.to(self.dtype). With the encoder stored in bf16,next(self.parameters())is a bf16 encoder parameter, so the image and theuvgrid would silently become bf16 (training used fp32 for both), and for v3torch.concat([features, uv])would stay bf16 and mismatch the fp32 refiner. Reading the neck's dtype gives fp32 under mixed precision, fp16 after.half(), and fp32 for a plain model, i.e. it is unchanged for all existing code paths (self.dtypeis only used ininfer()).Validation
cpufor the test): encoder parameters bf16, all other modules fp32,model.dtype == torch.float32,infer()runs end to end with fp32 outputs of the same shapes; median relative depth deviation vs. the fp32 run 2.5e-4 (bf16-level noise).moge infer --helprenders the new option;--bf16with--version v1or together with--fp16raises aUsageError.train_moge3.py --precision mixed_bf16; the only new runtime code isself.encoder.to(dtype)and the neck-baseddtypeproperty.