Text-to-Image
MLX
Safetensors
lance
multimodal
apple-silicon
image-generation
video-generation
diffusion
flow-matching
Mixture of Experts
qwen2_5_vl
wan
port
Instructions to use RockTalk/Lance-3B-MLX with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use RockTalk/Lance-3B-MLX with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir Lance-3B-MLX RockTalk/Lance-3B-MLX
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Initial release: Lance-3B-MLX first MLX port, T2I verified end-to-end
Browse files- .gitattributes +3 -0
- README.md +121 -69
- config.json +6 -5
- samples/orange_cat_chair.png +3 -0
- samples/snowy_peak.png +3 -0
- samples/sunset_mountains.png +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
samples/orange_cat_chair.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
samples/snowy_peak.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
samples/sunset_mountains.png filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -3,111 +3,163 @@ license: apache-2.0
|
|
| 3 |
base_model:
|
| 4 |
- bytedance-research/Lance
|
| 5 |
- Qwen/Qwen2.5-VL-3B-Instruct
|
| 6 |
-
pipeline_tag:
|
| 7 |
library_name: mlx
|
| 8 |
tags:
|
| 9 |
- multimodal
|
| 10 |
- mlx
|
| 11 |
- apple-silicon
|
|
|
|
| 12 |
- image-generation
|
| 13 |
- video-generation
|
| 14 |
-
-
|
| 15 |
-
-
|
| 16 |
-
-
|
|
|
|
|
|
|
| 17 |
- port
|
| 18 |
---
|
| 19 |
|
| 20 |
# Lance-3B-MLX
|
| 21 |
|
| 22 |
-
|
| 23 |
|
| 24 |
-
|
| 25 |
|
| 26 |
-
##
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|---|---|
|
| 34 |
-
|
|
| 35 |
-
|
|
| 36 |
-
|
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
| 43 |
-
|
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
`latent_pos_embed`, `vae_model.*`). The MoE-gen generation-path weights are
|
| 49 |
-
included verbatim so any future MoE-aware wrapper can pick them up.
|
| 50 |
-
|
| 51 |
-
**What's missing for end-to-end inference:** the Python wrapper class needs
|
| 52 |
-
(1) MoE-routing attention layers with parallel und/gen weight sets, (2)
|
| 53 |
-
QK-norm, (3) a corrected patchify path before `vae2llm`, (4) the
|
| 54 |
-
flow-matching denoising loop + CFG. The source PT implementation in
|
| 55 |
-
`Lance/modeling/lance/qwen2_navit.py` (~1300 lines) and `lance.py` (~1900
|
| 56 |
-
lines) is the reference.
|
| 57 |
|
| 58 |
## Files
|
| 59 |
|
| 60 |
-
|
| 61 |
-
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
| 66 |
|
| 67 |
-
##
|
| 68 |
|
| 69 |
-
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
| 72 |
|
| 73 |
```python
|
| 74 |
import mlx.core as mx
|
| 75 |
-
import mlx.nn as nn
|
| 76 |
from lance_mlx.lance import Lance, LanceConfig
|
| 77 |
-
from
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
-
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
| 87 |
```
|
| 88 |
|
| 89 |
-
|
| 90 |
|
| 91 |
-
```bibtex
|
| 92 |
-
@article{lance2026,
|
| 93 |
-
title = {Lance: Unified Multimodal Modeling by Multi-Task Synergy},
|
| 94 |
-
author = {Fu, Fengyi and Huang, Mengqi and Wu, Shaojin and Jiang, Yunsheng and Huo, Yufei and Guo, Jianzhu and others},
|
| 95 |
-
journal = {arXiv preprint arXiv:2605.18678},
|
| 96 |
-
year = {2026},
|
| 97 |
-
url = {http://arxiv.org/abs/2605.18678}
|
| 98 |
-
}
|
| 99 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
## License
|
| 102 |
|
| 103 |
-
Apache
|
| 104 |
|
| 105 |
-
##
|
| 106 |
|
| 107 |
-
- ByteDance Research
|
| 108 |
-
-
|
| 109 |
-
-
|
|
|
|
|
|
|
| 110 |
|
| 111 |
-
|
| 112 |
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
base_model:
|
| 4 |
- bytedance-research/Lance
|
| 5 |
- Qwen/Qwen2.5-VL-3B-Instruct
|
| 6 |
+
pipeline_tag: text-to-image
|
| 7 |
library_name: mlx
|
| 8 |
tags:
|
| 9 |
- multimodal
|
| 10 |
- mlx
|
| 11 |
- apple-silicon
|
| 12 |
+
- text-to-image
|
| 13 |
- image-generation
|
| 14 |
- video-generation
|
| 15 |
+
- diffusion
|
| 16 |
+
- flow-matching
|
| 17 |
+
- moe
|
| 18 |
+
- qwen2_5_vl
|
| 19 |
+
- wan
|
| 20 |
- port
|
| 21 |
---
|
| 22 |
|
| 23 |
# Lance-3B-MLX
|
| 24 |
|
| 25 |
+
First native [MLX](https://github.com/ml-explore/mlx) port of [ByteDance Research's Lance](https://huggingface.co/bytedance-research/Lance) β a 3B-parameter unified multimodal model for image/video generation, editing, and understanding. Runs natively on Apple Silicon, no CUDA required.
|
| 26 |
|
| 27 |
+
The architecture is **Qwen2.5-VL-3B + parallel MoE-gen experts + Wan 2.2 VAE**. Lance uses a "Mixture-of-Tokens" routing: every attention block and MLP has a parallel `*_moe_gen` branch. Text tokens go through normal weights; VAE-latent (generation) tokens go through the `_moe_gen` weights, in the same forward pass.
|
| 28 |
|
| 29 |
+
## What works
|
| 30 |
|
| 31 |
+
| Capability | Status |
|
| 32 |
+
|---|---|
|
| 33 |
+
| Text-to-image (T2I), single image, CFG | β
Working, verified |
|
| 34 |
+
| Strict load of all 1021 LLM/adapter tensors | β
Working |
|
| 35 |
+
| Wan 2.2 VAE encode/decode (T=1) | β
Working (uses [RockTalk/Wan2.2-VAE-MLX](https://huggingface.co/RockTalk/Wan2.2-VAE-MLX)) |
|
| 36 |
+
| Flow-matching denoising loop | β
Working |
|
| 37 |
+
| Classifier-free guidance | β
Working |
|
| 38 |
+
| 3D mrope position embeddings | β
Working |
|
| 39 |
+
| MoE-gen routing (per-token attention + MLP + layernorm) | β
Working |
|
| 40 |
+
| Text-to-video (T2V) | β³ Needs VAE T>1 streaming cache + larger pos embed |
|
| 41 |
+
| Image/video editing (TI2I, TIV2V) | β³ Phase 2 β needs ViT integration |
|
| 42 |
+
| XβT (image/video understanding) | β³ Phase 2 β needs AR sampling loop + KV cache |
|
| 43 |
+
|
| 44 |
+
## Sample generations
|
| 45 |
+
|
| 46 |
+
Verified on M4 Studio (128 GB). 30 steps, CFG=4, 512Γ512:
|
| 47 |
+
|
| 48 |
+
| Prompt | Output |
|
| 49 |
|---|---|
|
| 50 |
+
| *"a photo of a sunset over mountains"* |  |
|
| 51 |
+
| *"a fluffy orange cat sitting on a wooden chair, photorealistic"* |  |
|
| 52 |
+
| *"a majestic snowy mountain peak with a dramatic blue sky and clouds"* |  |
|
| 53 |
+
|
| 54 |
+
## Performance
|
| 55 |
+
|
| 56 |
+
Measured on M4 Studio (128 GB) at CFG=4 (one conditional + one unconditional forward per step):
|
| 57 |
+
|
| 58 |
+
| Resolution | Steps | Per-step | Total sample | VAE decode |
|
| 59 |
+
|---|---|---|---|---|
|
| 60 |
+
| 256Γ256 | 24 | ~400 ms | ~9.6 s | ~0.1 s |
|
| 61 |
+
| 512Γ512 | 30 | ~1.2 s | ~36 s | ~0.5 s |
|
| 62 |
+
|
| 63 |
+
First-call kernel-compile penalty: ~few seconds per new resolution.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
## Files
|
| 66 |
|
| 67 |
+
| File | Size | Description |
|
| 68 |
+
|---|---|---|
|
| 69 |
+
| `model.safetensors` | 23 GB | LLM (Qwen2.5-VL with MoE-gen) + Lance adapters, 1021 tensors |
|
| 70 |
+
| `vit.safetensors` | 1.25 GB | Qwen2.5-VL ViT (for understanding mode β Phase 2) |
|
| 71 |
+
| `vae.safetensors` | 2.62 GB | Wan 2.2 VAE (older keying β for compatibility; the standalone [RockTalk/Wan2.2-VAE-MLX](https://huggingface.co/RockTalk/Wan2.2-VAE-MLX) uses cleaner keys and is recommended) |
|
| 72 |
+
| `config.json` | β | Distilled architecture config |
|
| 73 |
+
| `tokenizer.json`, `vocab.json`, `merges.txt` | β | Qwen2.5-VL tokenizer, verbatim |
|
| 74 |
+
| `samples/*.png` | β | Verified T2I outputs from this checkpoint |
|
| 75 |
|
| 76 |
+
## Usage
|
| 77 |
|
| 78 |
+
Requires `mlx >= 0.29`, `mlx-vlm >= 0.3`, `numpy`, `einops`, `transformers`, `pillow`, and the [`lance-mlx`](https://github.com/RockTalk/Lance-MLX) companion repo for the `Lance` Python class.
|
| 79 |
|
| 80 |
+
```bash
|
| 81 |
+
pip install mlx mlx-vlm numpy einops transformers pillow
|
| 82 |
+
```
|
| 83 |
|
| 84 |
```python
|
| 85 |
import mlx.core as mx
|
|
|
|
| 86 |
from lance_mlx.lance import Lance, LanceConfig
|
| 87 |
+
from lance_mlx.vae_wan22 import Wan2_2_VAE
|
| 88 |
+
|
| 89 |
+
# Build + strict-load (see tools/lance_t2i.py in the companion repo for the
|
| 90 |
+
# full builder; LanceConfig takes a Qwen2.5-VL ModelConfig built from
|
| 91 |
+
# config.json).
|
| 92 |
+
model = Lance(lance_cfg)
|
| 93 |
+
model.load_weights(list(mx.load("model.safetensors").items()), strict=True)
|
| 94 |
+
|
| 95 |
+
vae = Wan2_2_VAE(z_dim=48, c_dim=160, dim_mult=(1, 2, 4, 4),
|
| 96 |
+
temperal_downsample=(False, True, True))
|
| 97 |
+
vae.model.load_weights(list(mx.load("vae.safetensors").items()), strict=True)
|
| 98 |
+
|
| 99 |
+
# Sample
|
| 100 |
+
latent = model.sample_t2i(
|
| 101 |
+
prompt_token_ids=text_ids, # (P,) int32 from tokenizer (no specials)
|
| 102 |
+
latent_shape=(1, 32, 32), # (T_lat, H_lat, W_lat) for 512Γ512 image
|
| 103 |
+
special_token_ids={"bos": 151644, "eos": 151645,
|
| 104 |
+
"start_of_image": 151652, "end_of_image": 151653,
|
| 105 |
+
"image_token_id": 151655},
|
| 106 |
+
num_steps=30, timestep_shift=3.5, cfg_scale=4.0, seed=0,
|
| 107 |
+
)
|
| 108 |
+
img = vae.decode(latent) # (1, 1, 512, 512, 3) in [-1, 1]
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
End-to-end script: `tools/lance_t2i.py` in the [companion repo](https://github.com/RockTalk/Lance-MLX).
|
| 112 |
|
| 113 |
+
## How the MoE-gen routing is implemented in MLX
|
| 114 |
|
| 115 |
+
Lance's checkpoint contains *two* sets of weights per Qwen2 block:
|
| 116 |
+
|
| 117 |
+
```
|
| 118 |
+
self_attn.{q,k,v,o}_proj self_attn.{q,k,v,o}_proj_moe_gen
|
| 119 |
+
self_attn.{q,k}_norm self_attn.{q,k}_norm_moe_gen
|
| 120 |
+
mlp.{gate,down,up}_proj mlp_moe_gen.{gate,down,up}_proj
|
| 121 |
+
input_layernorm input_layernorm_moe_gen
|
| 122 |
+
post_attention_layernorm post_attention_layernorm_moe_gen
|
| 123 |
```
|
| 124 |
|
| 125 |
+
For T2I/T2V the sequence layout is:
|
| 126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
```
|
| 128 |
+
<|im_start|> [prompt tokens] <|im_end|> <|vision_start|> [N latent placeholders] <|vision_end|>
|
| 129 |
+
βββββ routed through moe_gen βββββ
|
| 130 |
+
β everything else: normal weights
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
The MLX port (`qwen2_navit_mlx.py`) routes by slicing the sequence into the latent slab vs the surrounding text, applying the appropriate expert to each slab, and concatenating. mrope position ids continue to flow normally across both slabs (with axis-T/H/W coordinates only varying inside the latent slab).
|
| 134 |
+
|
| 135 |
+
## Conversion source
|
| 136 |
+
|
| 137 |
+
Converted from `bytedance-research/Lance/Lance_3B/*` using the open-source pipeline at https://github.com/RockTalk/Lance-MLX (`tools/convert_weights.py`). Layout transforms:
|
| 138 |
+
|
| 139 |
+
- Conv weights: PT `(O, I, [T,] H, W)` β MLX `(O, [T,] H, W, I)`
|
| 140 |
+
- Embedding weights: shape preserved
|
| 141 |
+
- `lm_head.weight` tied to `embed_tokens.weight` (Qwen default)
|
| 142 |
+
- All `*_moe_gen.*` keys copied verbatim under the same names
|
| 143 |
|
| 144 |
## License
|
| 145 |
|
| 146 |
+
Apache 2.0, inherited from upstream `bytedance-research/Lance`. The Wan 2.2 VAE component is also Apache 2.0 from Alibaba's Wan team.
|
| 147 |
|
| 148 |
+
## Acknowledgements
|
| 149 |
|
| 150 |
+
- **ByteDance Research** β original Lance training + PT release
|
| 151 |
+
- **Qwen team** β Qwen2.5-VL-3B-Instruct backbone
|
| 152 |
+
- **Alibaba Wan team** β Wan 2.2 VAE training
|
| 153 |
+
- **Apple `mlx` and `mlx-vlm` teams** β the underlying frameworks
|
| 154 |
+
- **This MLX port** β RockTalk
|
| 155 |
|
| 156 |
+
## Citation
|
| 157 |
|
| 158 |
+
```bibtex
|
| 159 |
+
@misc{lance_mlx,
|
| 160 |
+
title = {Lance-3B-MLX β First MLX port of ByteDance's Lance},
|
| 161 |
+
author = {RockTalk},
|
| 162 |
+
year = {2026},
|
| 163 |
+
url = {https://huggingface.co/RockTalk/Lance-3B-MLX}
|
| 164 |
+
}
|
| 165 |
+
```
|
config.json
CHANGED
|
@@ -67,14 +67,15 @@
|
|
| 67 |
},
|
| 68 |
"latent_patch_size": [
|
| 69 |
1,
|
| 70 |
-
|
| 71 |
-
|
| 72 |
],
|
| 73 |
-
"max_latent_size":
|
| 74 |
-
"max_num_frames":
|
| 75 |
"latent_channel": 48,
|
| 76 |
"vae_downsample_spatial": 16,
|
| 77 |
"vae_downsample_temporal": 4,
|
| 78 |
"connector_act": "gelu_pytorch_tanh",
|
| 79 |
-
"timestep_shift": 3.5
|
|
|
|
| 80 |
}
|
|
|
|
| 67 |
},
|
| 68 |
"latent_patch_size": [
|
| 69 |
1,
|
| 70 |
+
1,
|
| 71 |
+
1
|
| 72 |
],
|
| 73 |
+
"max_latent_size": 64,
|
| 74 |
+
"max_num_frames": 0,
|
| 75 |
"latent_channel": 48,
|
| 76 |
"vae_downsample_spatial": 16,
|
| 77 |
"vae_downsample_temporal": 4,
|
| 78 |
"connector_act": "gelu_pytorch_tanh",
|
| 79 |
+
"timestep_shift": 3.5,
|
| 80 |
+
"max_num_latent_frames": 1
|
| 81 |
}
|
samples/orange_cat_chair.png
ADDED
|
Git LFS Details
|
samples/snowy_peak.png
ADDED
|
Git LFS Details
|
samples/sunset_mountains.png
ADDED
|
Git LFS Details
|