Repository navigation
Add Bria fibo-2 - #14991
Add Bria fibo-2#14991muhammadali-afridi wants to merge 8 commits into
Conversation
e3fdebc to
2b7b792
Compare
553af68 to
5c38cb1
Compare
fibo-2 is Bria's 8.1B flow-matching DiT, built on the Z-Image block and
conditioned on Qwen3-VL hidden states two ways: a Perceiver resampler turns
them into gist tokens that share the stream with the image tokens, and five
blocks add gated cross-attention to their own bundle of Qwen3-VL layers. It
uses the Flux 2 VAE.
One pipeline class covers both of the model's jobs and both checkpoints:
text-to-image generation, instruction editing of up to five images with an
optional mask, the turbo checkpoint (4 steps, no guidance) and the guided one.
- BriaFibo2Transformer2DModel, with images to edit passed as context latents
- BriaFibo2Pipeline, including:
- captions cleaned and serialized the way training did, for generation and
edits, in every caption form fibo-2 was trained with: a full caption, a
short description, or a short description with its text_render list
- images of the caption's text, which Qwen3-VL sees beside the prompt:
drawn the way training drew them, by default (`render_text_images`), or
passed as `text_images`
- masked edits, the region greyed out through a binary mask as in training
- a conversion script from the original checkpoint layout
- model and pipeline tests, and docs
Verified against Bria's reference implementation on an A100 and an H100.
5c38cb1 to
4f7c850
Compare
sayakpaul
left a comment
There was a problem hiding this comment.
Looks pretty good actually! Can we see some examples, please?
Also, we can remove tests where we're calling private methods.
| def apply_rotary_emb(x_in: torch.Tensor, freqs_cis: torch.Tensor) -> torch.Tensor: | ||
| with torch.amp.autocast("cuda", enabled=False): | ||
| x = torch.view_as_complex(x_in.float().reshape(*x_in.shape[:-1], -1, 2)) | ||
| freqs_cis = freqs_cis.unsqueeze(2) | ||
| x_out = torch.view_as_real(x * freqs_cis).flatten(3) | ||
| return x_out.type_as(x_in) |
There was a problem hiding this comment.
We usually avoid autocast blocks within the code. Possible to explicitly handle the casting?
There was a problem hiding this comment.
Sure. The same block is in TimestepEmbedder, which is # copied from Z-Image’s. will change it there too if you’d like.
| @pytest.mark.skip( | ||
| "RopeEmbedder, copied from Z-Image, builds its frequency table under a `torch.device` context on first use, " | ||
| "which Dynamo cannot trace in a full graph. Z-Image skips this test too." | ||
| ) | ||
| def test_torch_compile_recompilation_and_graph_break(self): | ||
| pass |
| @pytest.mark.skip( | ||
| "RopeEmbedder, copied from Z-Image, cannot be traced in a full graph (see above). Z-Image skips this test too." | ||
| ) | ||
| def test_compile_on_different_shapes(self): | ||
| pass |
| def test_edit_prompt(self): | ||
| # The prompt shape fibo-2 was trained to edit with: a vision marker per image, the cleaned caption, then the | ||
| # instruction | ||
| pipe = self.get_pipeline() | ||
| marker = "<|vision_start|><|image_pad|><|vision_end|>" | ||
| caption = { | ||
| "short_description": "a blue bicycle", | ||
| "objects": [], | ||
| "aesthetics": {"mood": "calm", "aesthetic_score": "high"}, | ||
| "edit_instruction": "Paint <image_1> like <image_2>", | ||
| } | ||
|
|
||
| assert pipe._get_edit_prompt("Make it blue", 1) == f'{{"image":"{marker}","edit_instruction":"Make it blue"}}' | ||
| assert pipe._get_edit_prompt(json.dumps(caption), 2) == ( | ||
| f'{{"image_1":"{marker}","image_2":"{marker}","structured_caption":{{"short_description":"a blue bicycle",' | ||
| f'"aesthetics":{{"mood":"calm"}}}},"edit_instruction":"Paint <image_1> like <image_2>"}}' | ||
| ) |
There was a problem hiding this comment.
We can remove tests for methods are private.
The rotation already runs in float32 and casts back to the input's dtype, and none of its ops are autocast ops, so the autocast block changed nothing.
…pping them Both fail with torch._dynamo.exc.Unsupported, on the torch.device context in which RopeEmbedder, copied from Z-Image, builds its frequency table. Strict, so they show up once Dynamo can trace it.
Removes the tests of private prompt and text-image helpers. The caption cleaning test now checks that a caption renders like its trained form, and the text images test draws its strips with the public draw_text_image.
|
@sayakpaul, regarding the example, let me confirm with my team about the best way to share weights with you since we don't plan to make it public before the official release |
|
Cool, we can merge once the date is up. |
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
| encoder_attention_mask: torch.Tensor | None = None, | ||
| context_latents: list[torch.Tensor] | None = None, | ||
| return_dict: bool = True, | ||
| ) -> torch.Tensor | Transformer2DModelOutput: |
There was a problem hiding this comment.
Looks like this should be a tuple[torch.Tensor]? I know that this persists across different models under src/diffusers/models (which we can fix in a different PR).
There was a problem hiding this comment.
you are right. fixed
| edit["edit_instruction"] = instruction | ||
| return json.dumps(edit, separators=(",", ":"), ensure_ascii=False) | ||
|
|
||
| def _get_qwen_prompt_embeds( |
There was a problem hiding this comment.
Is it possible to make it a staticmethod and also pass the processor and text_encoder?
There was a problem hiding this comment.
sure. now it takes text_encoder and processor, plus the template, max length and layers (defaulting to module constants), like Flux 2’s _get_mistral_3_small_prompt_embeds.
…er and processor The chat template, the sequence length and the Qwen3-VL layers become arguments, defaulting to module constants; encode_prompt passes the pipeline's own. The edit and text prompt helpers become classmethods. Embeddings are unchanged.
…-2.0-turbo The docs and the example load Fibo-2.0-dev and show the turbo checkpoint's 4-step edits without guidance. Text-image fonts download from Fibo-2.0-dev, and the conversion script's usage converts a local original checkpoint.
What does this PR do?
Adds fibo-2, Bria's text-to-image and image-editing model.
BriaFibo2Transformer2DModel: an 8.1B flow-matching transformer with a single token stream, in the style of Z-Image. Its text encoder is Qwen3-VL-4B.context_latents. Each is patchified by the shared embedder and gets its own RoPE plane.BriaFibo2Pipeline:"structured_caption": "..."in an edit), or a short description with itstext_renderlist;render_text_images=Falseturns them off), or the caller passes them astext_images.briaai/Fibo-2.0-dev, the full model, runs 30 steps with guidance 5, the pipeline's defaults.briaai/Fibo-2.0-turbois distilled for editing in 4 steps, withguidance_scale=1.0turning guidance off.scripts/convert_bria_fibo2_to_diffusers.pyconverts the original checkpoints.Docs and tests.
Weights:
briaai/Fibo-2.0-devandbriaai/Fibo-2.0-turbo, in diffusers format. They're private until the release, and we're happy to give reviewers early access.Verification
I compared the pipeline against Bria's reference inference code, using the original checkpoints, the same seeds and bf16:
A100, SDPA:
H100: the reference code runs FlashAttention-3 there. With
pipe.transformer.set_attention_backend("_flash_3_varlen_hub"):Prompts and text images: compared with Bria's training and evaluation code, every prompt is byte-identical:
The drawn images are also byte-identical on 17 test strings, including non-Latin scripts, wrapped lines and characters no font has.
Text-encoder images: by default the pipeline thumbnails each image straight to 768 px for Qwen3-VL, as in training. The reference code upsamples 4× first, which changes that input slightly.
Batching: in float32, a batch of prompts of different lengths matches the separate single-prompt calls. This includes the varlen attention backends; see the self-review notes.
Checks (rebased on
main):make style,make qualityandmake fix-copiespass.check_copies,check_dummies,check_support_list,check_forward_call_docstringsandcheck_return_annotationspass.New tests: they pass on a Mac (CPU/MPS), except the group offloading and layerwise casting memory tests. Those fail the same way for Z-Image and Flux 2 on that machine, because MPS has no CUDA streams and no float8.
Notes
mask: fibo-2 was trained on edits whose image has the region to regenerate greyed out. The pipeline does the same: flat (128, 128, 128) grey, through the mask made binary at 128.fontsfolder ofbriaai/Fibo-2.0-devonly when a text first needs them, so ASCII text never downloads anything. The fonts are in both release repos.aesthetic_scoreandpreference_scorefields. The pipeline drops them from text-to-image and edit captions alike and serializes compactly. The negative prompt is used as given.Self-review notes
I ran the
self-reviewskill on the diff.Fixed:
forwarddocstring had noReturns:section, whichcheck_forward_call_docstringsflags.flash_varlen,_flash_3_varlen_hub,sage_varlen) keep the firstmask.sum()keys, so they kept the wrong ones.test_padded_batch_matches_single_promptscovers this.__call__read the first image's size beforecheck_inputshad validated it.Not fixed, on purpose:
RopeEmbedder, copied from Z-Image, builds its frequency table under a device context on first use. Z-Image skips these tests for the same reason.torch.compile(transformer)..tolist().compile_repeated_blocks) is not affected.Before submitting
self-reviewskill on the diff?Who can review?
@yiyixuxu @sayakpaul @DN6
🤖 Generated with Claude Code