Stage 1 checkpoint (projector warmup on LLaVA-CC3M-Pretrain-595K)
Browse files- README.md +68 -10
- modeling_baguettotron_vlm.py +5 -3
README.md
CHANGED
|
@@ -21,18 +21,33 @@ base_model:
|
|
| 21 |
|
| 22 |
# Baguettotron-VLM β Stage 1 (Projector Warmup)
|
| 23 |
|
| 24 |
-
**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
- Stage
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
## Architecture
|
| 38 |
|
|
@@ -93,6 +108,49 @@ Stage 1 was trained on short image captions with no `<think>` traces. The proces
|
|
| 93 |
| Hardware | 1Γ H100 SXM (RunPod) |
|
| 94 |
| Duration | ~5h |
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
## License
|
| 97 |
|
| 98 |
Apache 2.0 β see the [GitHub repo](https://github.com/andreagemelli/baguettotron-vlm).
|
|
|
|
| 21 |
|
| 22 |
# Baguettotron-VLM β Stage 1 (Projector Warmup)
|
| 23 |
|
| 24 |
+
**Baguettotron-VLM** is an open, fully-reproducible, multilingual Vision-Language
|
| 25 |
+
Model in the **sub-1B parameter** class. It extends
|
| 26 |
+
[PleIAs/Baguettotron](https://huggingface.co/PleIAs/Baguettotron) β a 321M
|
| 27 |
+
text-only reasoning SLM β with visual capabilities via the
|
| 28 |
+
[InternViT-300M-448px-V2.5](https://huggingface.co/OpenGVLab/InternViT-300M-448px-V2_5)
|
| 29 |
+
vision encoder and a lightweight MLP projector, for a total of ~628M parameters.
|
| 30 |
|
| 31 |
+
It supports **six European languages** (EN, FR, DE, ES, IT, PL, inherited from
|
| 32 |
+
the Baguettotron backbone), natively emits `<think>` reasoning traces, and was
|
| 33 |
+
trained end-to-end on a single H100 SXM β code, data pipeline, weights, and
|
| 34 |
+
training logs are all public. Everything is released under **Apache 2.0** with
|
| 35 |
+
a proof-of-concept goal: exploring what an open sub-1B multilingual VLM can do,
|
| 36 |
+
not chasing SOTA.
|
| 37 |
|
| 38 |
+
The project ships three checkpoints from a staged training curriculum:
|
| 39 |
+
|
| 40 |
+
- **Stage 1 β projector warmup**: [andreagemelli/Baguettotron-VLM-Stage1](https://huggingface.co/andreagemelli/Baguettotron-VLM-Stage1)
|
| 41 |
+
(MLP projector only, LLaVA-CC3M-Pretrain-595K β published for reproducibility).
|
| 42 |
+
- **Stage 2 β instruction tuning**: [andreagemelli/Baguettotron-VLM-Stage2](https://huggingface.co/andreagemelli/Baguettotron-VLM-Stage2)
|
| 43 |
+
(full fine-tune on The Cauldron + SYNTH, direct-answer mode).
|
| 44 |
+
- **Stage 3 β reasoning SFT (flagship)**: [andreagemelli/Baguettotron-VLM](https://huggingface.co/andreagemelli/Baguettotron-VLM)
|
| 45 |
+
(reasoning traces via R1-Vision-Reasoning-Instructions, dynamic `<think>` toggle).
|
| 46 |
+
|
| 47 |
+
Source & training pipeline: [github.com/andreagemelli/baguettotron-vlm](https://github.com/andreagemelli/baguettotron-vlm).
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
> **Stage 1 checkpoint.** Only the MLP projector has been trained (on LLaVA-CC3M-Pretrain-595K); the ViT and the Baguettotron LLM are the unmodified base weights. Published for reproducibility β for actual use prefer the Stage 2 or Stage 3 checkpoints.
|
| 51 |
|
| 52 |
## Architecture
|
| 53 |
|
|
|
|
| 108 |
| Hardware | 1Γ H100 SXM (RunPod) |
|
| 109 |
| Duration | ~5h |
|
| 110 |
|
| 111 |
+
## Project scope & limitations
|
| 112 |
+
|
| 113 |
+
Baguettotron-VLM is a **self-educational, open-source proof of concept** trained
|
| 114 |
+
on a limited compute budget. The goal has always been to explore what an open,
|
| 115 |
+
fully-reproducible, multilingual sub-1B VLM can do when every design choice has
|
| 116 |
+
to justify itself against a hard budget cap.
|
| 117 |
+
|
| 118 |
+
Concretely, this means the model may have limitations:
|
| 119 |
+
|
| 120 |
+
- **Likely undertrained for VQA alignment.** The Stage 2 instruction-tuning
|
| 121 |
+
budget (~60h on a single H100) is small relative to the data mixture; the
|
| 122 |
+
model has seen far fewer tokens than typical VLMs of comparable size.
|
| 123 |
+
- **Hallucinations.** The Baguettotron backbone is pretrained on synthetic
|
| 124 |
+
corpora and reasoning traces, which gives it strong `<think>` behaviour but
|
| 125 |
+
also a tendency to confabulate details that aren't grounded in the image,
|
| 126 |
+
especially on fine-grained or text-heavy questions.
|
| 127 |
+
- **Best on simple, scene-level prompts.** In informal testing the model is
|
| 128 |
+
most reliable at open-ended description and scene understanding
|
| 129 |
+
("what is happening in this image?") and less reliable at OCR, counting,
|
| 130 |
+
chart reading, or multi-hop visual reasoning. Quantitative benchmark
|
| 131 |
+
numbers will be published once the evaluation harness lands.
|
| 132 |
+
|
| 133 |
+
**Contributions and suggestions are very welcome** β issues, PRs, and ideas for
|
| 134 |
+
better data mixes, training recipes, or evaluation setups are all appreciated.
|
| 135 |
+
Open an issue or PR on the
|
| 136 |
+
[GitHub repo](https://github.com/andreagemelli/baguettotron-vlm).
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
## Citation
|
| 140 |
+
|
| 141 |
+
If you use or extend Baguettotron-VLM in your research, please cite it:
|
| 142 |
+
|
| 143 |
+
```bibtex
|
| 144 |
+
@misc{gemelli2026baguettotronvlm,
|
| 145 |
+
title = {Baguettotron-VLM: An Open, Reproducible, Multilingual Sub-1B Vision-Language Model},
|
| 146 |
+
author = {Gemelli, Andrea},
|
| 147 |
+
year = {2026},
|
| 148 |
+
howpublished = {\url{https://huggingface.co/andreagemelli/Baguettotron-VLM}},
|
| 149 |
+
note = {Code: \url{https://github.com/andreagemelli/baguettotron-vlm}}
|
| 150 |
+
}
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
|
| 154 |
## License
|
| 155 |
|
| 156 |
Apache 2.0 β see the [GitHub repo](https://github.com/andreagemelli/baguettotron-vlm).
|
modeling_baguettotron_vlm.py
CHANGED
|
@@ -83,7 +83,7 @@ class BaguettotronVLMForConditionalGeneration(PreTrainedModel):
|
|
| 83 |
self.llm = AutoModelForCausalLM.from_pretrained(
|
| 84 |
config.llm_model_id, dtype=torch.bfloat16
|
| 85 |
)
|
| 86 |
-
self.llm.resize_token_embeddings(len(tokenizer))
|
| 87 |
# Break weight tying β safetensors rejects shared-storage tensors
|
| 88 |
self.llm.lm_head.weight = nn.Parameter(self.llm.lm_head.weight.data.clone())
|
| 89 |
|
|
@@ -109,12 +109,13 @@ class BaguettotronVLMForConditionalGeneration(PreTrainedModel):
|
|
| 109 |
inputs_embeds = self.llm.get_input_embeddings()(input_ids)
|
| 110 |
|
| 111 |
if pixel_values is not None:
|
|
|
|
| 112 |
with torch.no_grad():
|
| 113 |
vit_out = self.vit(pixel_values)
|
| 114 |
image_features = vit_out.last_hidden_state
|
| 115 |
if image_features.shape[1] == self.config.vit_tokens + 1:
|
| 116 |
image_features = image_features[:, 1:, :]
|
| 117 |
-
visual_tokens = self.projector(image_features.
|
| 118 |
image_mask = input_ids == self.image_token_id
|
| 119 |
inputs_embeds[image_mask] = visual_tokens.reshape(
|
| 120 |
-1, self.config.llm_hidden
|
|
@@ -146,11 +147,12 @@ class BaguettotronVLMForConditionalGeneration(PreTrainedModel):
|
|
| 146 |
inputs_embeds = self.llm.get_input_embeddings()(input_ids)
|
| 147 |
|
| 148 |
if pixel_values is not None:
|
|
|
|
| 149 |
vit_out = self.vit(pixel_values)
|
| 150 |
image_features = vit_out.last_hidden_state
|
| 151 |
if image_features.shape[1] == self.config.vit_tokens + 1:
|
| 152 |
image_features = image_features[:, 1:, :]
|
| 153 |
-
visual_tokens = self.projector(image_features.
|
| 154 |
image_mask = input_ids == self.image_token_id
|
| 155 |
inputs_embeds[image_mask] = visual_tokens.reshape(
|
| 156 |
-1, self.config.llm_hidden
|
|
|
|
| 83 |
self.llm = AutoModelForCausalLM.from_pretrained(
|
| 84 |
config.llm_model_id, dtype=torch.bfloat16
|
| 85 |
)
|
| 86 |
+
self.llm.resize_token_embeddings(len(tokenizer), mean_resizing=False)
|
| 87 |
# Break weight tying β safetensors rejects shared-storage tensors
|
| 88 |
self.llm.lm_head.weight = nn.Parameter(self.llm.lm_head.weight.data.clone())
|
| 89 |
|
|
|
|
| 109 |
inputs_embeds = self.llm.get_input_embeddings()(input_ids)
|
| 110 |
|
| 111 |
if pixel_values is not None:
|
| 112 |
+
pixel_values = pixel_values.to(dtype=self.vit.dtype, device=self.vit.device)
|
| 113 |
with torch.no_grad():
|
| 114 |
vit_out = self.vit(pixel_values)
|
| 115 |
image_features = vit_out.last_hidden_state
|
| 116 |
if image_features.shape[1] == self.config.vit_tokens + 1:
|
| 117 |
image_features = image_features[:, 1:, :]
|
| 118 |
+
visual_tokens = self.projector(image_features.to(inputs_embeds.dtype))
|
| 119 |
image_mask = input_ids == self.image_token_id
|
| 120 |
inputs_embeds[image_mask] = visual_tokens.reshape(
|
| 121 |
-1, self.config.llm_hidden
|
|
|
|
| 147 |
inputs_embeds = self.llm.get_input_embeddings()(input_ids)
|
| 148 |
|
| 149 |
if pixel_values is not None:
|
| 150 |
+
pixel_values = pixel_values.to(dtype=self.vit.dtype, device=self.vit.device)
|
| 151 |
vit_out = self.vit(pixel_values)
|
| 152 |
image_features = vit_out.last_hidden_state
|
| 153 |
if image_features.shape[1] == self.config.vit_tokens + 1:
|
| 154 |
image_features = image_features[:, 1:, :]
|
| 155 |
+
visual_tokens = self.projector(image_features.to(inputs_embeds.dtype))
|
| 156 |
image_mask = input_ids == self.image_token_id
|
| 157 |
inputs_embeds[image_mask] = visual_tokens.reshape(
|
| 158 |
-1, self.config.llm_hidden
|