Review
Browse files- app/src/content/article.mdx +131 -73
app/src/content/article.mdx
CHANGED
|
@@ -29,11 +29,11 @@ We continue to support all new models and expect to do so for the foreseeable fu
|
|
| 29 |
|
| 30 |
This post dissects the design philosophy that makes this possible. It's the result of a gradual evolution from our older principles, detailed on our previous [philosophy](https://huggingface.co/docs/transformers/en/philosophy) page, as well as its accompanying [blog post from 2022](https://huggingface.co/blog/transformers-design-philosophy). More recently (and I do recommend the read), we wrote a blog post about [recent upgrades to transformers](https://huggingface.co/blog/faster-transformers) with a special focus on what makes the library faster today. All of these developments were only made possible thanks to these principles.
|
| 31 |
|
| 32 |
-
|
| 33 |
|
| 34 |
For any OSS maintainer, power user, or contributor, this is the map to understanding, using, and building upon `transformers`; but not only: any project of comparable size will require you to make deep choices, not only on design and choice of abstractions, but on the very mindset of the software you are building. These tenets may or may not be applicable to your project, but they provide a glimpse on how we work that could be helpful or inspirational.
|
| 35 |
|
| 36 |
-
Conventions used
|
| 37 |
|
| 38 |
* [Tenets exemplified](#source-of-truth) will have their summary available on hover.
|
| 39 |
|
|
@@ -185,35 +185,41 @@ However, if a model has a modular_*.py and a corresponding automatically generat
|
|
| 185 |
|
| 186 |
That gives an "effective LOC" curve: the ๐บ๐ฎ๐ถ๐ป๐๐ฒ๐ป๐ฎ๐ป๐ฐ๐ฒ ๐๐๐ฟ๐ณ๐ฎ๐ฐ๐ฒ.
|
| 187 |
|
| 188 |
-
Measured on git history, raw `modeling_*.py` grew at ~362 LOC/day before modular; counting only modular shards yields ~25 LOC/day after โ about **15ร lower**. The curve represents the **maintenance surface** today: what maintainers actually read and review.
|
| 189 |
|
| 190 |
-
|
|
|
|
|
|
|
| 191 |
|
| 192 |
<HtmlEmbed src="transformers/loc-growth.html" />
|
| 193 |
|
| 194 |
-
|
|
|
|
|
|
|
| 195 |
|
| 196 |
-
|
| 197 |
|
| 198 |
-
|
| 199 |
|
| 200 |
-
|
| 201 |
|
| 202 |
-
However, we were adding specific torch operations for each backend (sdpa, flash-attention
|
| 203 |
|
| 204 |
<div class="crumbs">
|
| 205 |
-
Evidence: effective LOC drops ~15ร when counting shards instead of expanded modeling. Less to read, fewer places to break.
|
|
|
|
|
|
|
| 206 |
</div>
|
| 207 |
|
| 208 |
### <a id="attention-classes"></a> External Attention classes
|
| 209 |
|
| 210 |
-
The solution
|
| 211 |
|
| 212 |
-
|
| 213 |
|
| 214 |
-
|
| 215 |
|
| 216 |
-
This
|
| 217 |
|
| 218 |
```python
|
| 219 |
attention_interface: Callable = eager_attention_forward
|
|
@@ -221,9 +227,11 @@ if self.config._attn_implementation != "eager":
|
|
| 221 |
attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
|
| 222 |
```
|
| 223 |
|
| 224 |
-
A strength of the new attention interface is the possibility to enforce specific kwargs, which are needed by kernel providers and other dependencies. We know that kwargs are often a necessary evil that plagues tools with widespread compatibility;
|
| 225 |
|
| 226 |
-
|
|
|
|
|
|
|
| 227 |
|
| 228 |
```python
|
| 229 |
from typing import Annotated
|
|
@@ -233,42 +241,46 @@ MyModelOutputAnnotated = Annotated[MyModelOutput, "shape: (B, C, H, W)"]
|
|
| 233 |
|
| 234 |
|
| 235 |
<div class="crumbs">
|
| 236 |
-
|
|
|
|
|
|
|
| 237 |
</div>
|
| 238 |
|
| 239 |
### <a id="simpler-tensor-parallelism"></a> Configurable Tensor Parallelism
|
| 240 |
|
| 241 |
If you're not familiar with the different flavours of parallelism, I recommend checking out [this blog post](https://huggingface.co/blog/accelerate-nd-parallel) first, and of course a full [dive into the ultra-scale playbook](https://huggingface.co/spaces/nanotron/ultrascale-playbook) is always recommended.
|
| 242 |
|
| 243 |
-
The essential part is that, as [the documentation states](https://huggingface.co/docs/transformers/v4.56.2/perf_train_gpu_many#tensor-parallelism) when tensors get too large to fit on a single GPU, they are sliced along a particular dimension and every slice is sent to a different GPU.
|
| 244 |
|
| 245 |
Why does it matter?
|
| 246 |
|
| 247 |
Because we want to avoid code modifications that are unrelated to the model.
|
| 248 |
-
We choose to place the level of abstraction higher than the device placement: a matrix multiplication - a `nn.Linear` layer - should be always expressed in the same way, regardless of how it is placed.
|
| 249 |
-
|
| 250 |
-
Hence, we want to touch [minimally](#minimal-user-api) to the modeling code, and only modify it when _architectural changes_ are involved. For instance, for tensor parallelism, we instead now specify a simple `tp_plan`.
|
| 251 |
|
| 252 |
-
|
| 253 |
|
| 254 |
-
|
| 255 |
|
| 256 |
<HtmlEmbed src="transformers/tp-plan.html" />
|
| 257 |
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
-
|
| 260 |
|
| 261 |
`torchrun --nproc-per-node 4 demo.py`
|
| 262 |
|
| 263 |
-
Semantics stay in the model (a Linear stays a Linear),
|
| 264 |
|
| 265 |
<div class="crumbs">
|
| 266 |
-
|
|
|
|
|
|
|
| 267 |
</div>
|
| 268 |
|
| 269 |
### <a id="layers-attentions-caches"></a> Layers, attentions and caches
|
| 270 |
|
| 271 |
-
Following the same logic, the _nature_ of attention and
|
| 272 |
|
| 273 |
|
| 274 |
```python
|
|
@@ -281,7 +293,7 @@ ALLOWED_LAYER_TYPES = (
|
|
| 281 |
)
|
| 282 |
```
|
| 283 |
|
| 284 |
-
and the configuration can be _explicit_ about which attention type is in which layer,
|
| 285 |
|
| 286 |
```python
|
| 287 |
"layer_types": [
|
|
@@ -293,10 +305,12 @@ and the configuration can be _explicit_ about which attention type is in which l
|
|
| 293 |
],
|
| 294 |
```
|
| 295 |
|
| 296 |
-
This is [minimal](#minimal-user-api) to implement on the user side, and allows to keep the modeling untouched. It is also easy to tweak.
|
| 297 |
|
| 298 |
<div class="crumbs">
|
| 299 |
-
Allowed layer types are explicit; schedules (e.g., sliding/full alternation) live in config. This keeps the file readable and easy to tweak.
|
|
|
|
|
|
|
| 300 |
</div>
|
| 301 |
|
| 302 |
|
|
@@ -310,18 +324,20 @@ class GlmRMSNorm(nn.Module):
|
|
| 310 |
...
|
| 311 |
```
|
| 312 |
|
| 313 |
-
This also opens another contribution path: GPU specialists can contribute optimized kernels to the
|
| 314 |
|
| 315 |
Even more resources have been added, like the formidable [kernel builder](https://github.com/huggingface/kernel-builder) with its connected resources to [help you build kernels with it](https://github.com/huggingface/kernel-builder/blob/main/docs/writing-kernels.md) and [with nix](https://github.com/huggingface/kernel-builder/blob/main/docs/nix.md).
|
| 316 |
|
| 317 |
-
|
| 318 |
<div class="crumbs">
|
| 319 |
-
Models define semantics; kernels define how to run them faster. Use
|
|
|
|
|
|
|
| 320 |
</div>
|
| 321 |
|
| 322 |
-
## Modular
|
|
|
|
|
|
|
| 323 |
|
| 324 |
-
Now, we have a form of inheritance in our codebase. Some models become standards, and model contributors are given the opportunity to _define standards_. Pushing the boundaries of scientific knowledge can translate into the boundaries of engineering if this effort is made, and we're striving for it.
|
| 325 |
It's hard to conceptualize very large libraries and how their components interact with each other, regardless of your cognitive abilities for abstractions.
|
| 326 |
So I wanted to take a look at the current **state of modularity** across the repository. How many models are defined using components of others?
|
| 327 |
|
|
@@ -330,44 +346,55 @@ To get this graph, I used the heuristic of modular inheritance.
|
|
| 330 |
2. In this `modular` file, what models, configurations and processings are imported?
|
| 331 |
3. Recurse through the model list that way.
|
| 332 |
|
| 333 |
-
So what do we see? Llama is a basis for many models, and it shows.
|
| 334 |
Radically different architectures such as mamba have spawned their own dependency subgraph.
|
| 335 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
<HtmlEmbed src="transformers/dependency-graph.html" />
|
| 338 |
|
| 339 |
-
|
| 340 |
As you can see, there is a small DETR island, a little llava pocket, and so on, but it's not comparable to the centrality observed for llama.
|
| 341 |
|
| 342 |
-
Another problem is, this
|
| 343 |
|
| 344 |
How do we spot them, and how do we identify modularisable models?
|
| 345 |
|
| 346 |
<div class="crumbs">
|
| 347 |
-
|
|
|
|
|
|
|
| 348 |
</div>
|
| 349 |
|
| 350 |
|
| 351 |
### Many models, but not enough yet, are alike
|
| 352 |
|
| 353 |
-
|
| 354 |
-
|
| 355 |
|
| 356 |
-
It is interesting, for
|
| 357 |
|
| 358 |
Zoom out below - it's full of models. You can click on a node to see its connections better, or use the text box to search for a model.
|
| 359 |
|
| 360 |
<HtmlEmbed src="transformers/model-timeline.html" />
|
| 361 |
|
| 362 |
-
|
|
|
|
|
|
|
| 363 |
|
| 364 |
<div class="crumbs">
|
| 365 |
-
Similarity (Jaccard
|
|
|
|
|
|
|
| 366 |
</div>
|
| 367 |
|
| 368 |
### VLM improvements, avoiding abstraction
|
| 369 |
|
| 370 |
-
We don't have cookbook for common VLM patterns (image token scatter, multiโtower encoders, crossโ
|
| 371 |
|
| 372 |
For instance, we thought of abstracting away the mixing of `inputs_embeds`, the tensor fed into an LLM decoder in 95% of the existing VLMs. It would have looked like something like
|
| 373 |
|
|
@@ -376,7 +403,9 @@ class InputsEmbeddingMixerMixin(nn.Module):
|
|
| 376 |
#
|
| 377 |
```
|
| 378 |
|
| 379 |
-
But this is [abstracting away an important component of the modeling
|
|
|
|
|
|
|
| 380 |
|
| 381 |
What is the current state of these โabstractionsโ across the codebase?
|
| 382 |
You will see all the imports around a modeling file, here [Gemma3n](https://huggingface.co/google/gemma-3n-E4B-it).
|
|
@@ -433,22 +462,28 @@ The following [Pull request to standardize placeholder masking](https://github.c
|
|
| 433 |
|
| 434 |
But this is _within_ the modeling file, not in the `PreTrainedModel` base class. It will not move away from it, because it'd break the [self-contained logic](#one-model-one-file) of the model.
|
| 435 |
|
|
|
|
|
|
|
| 436 |
<div class="crumbs">
|
| 437 |
-
Keep VLM embedding mix in the modeling file (semantics), standardize safe helpers (e.g., placeholder masking), don't migrate behavior to <code>PreTrainedModel</code>.
|
|
|
|
|
|
|
| 438 |
</div>
|
| 439 |
|
| 440 |
|
| 441 |
### On image processing and processors
|
| 442 |
|
| 443 |
-
|
| 444 |
|
| 445 |
-
The gains in performance are immense, up to 20x
|
| 446 |
|
| 447 |

|
| 448 |
<p class="figure-legend">Thanks <a href="https://huggingface.co/yonigozlan">Yoni Gozlan</a> for the great work!</p>
|
| 449 |
|
| 450 |
<div class="crumbs">
|
| 451 |
-
|
|
|
|
|
|
|
| 452 |
</div>
|
| 453 |
|
| 454 |
|
|
@@ -458,82 +493,93 @@ This is an overall objective: there's no `transformers` without its community.
|
|
| 458 |
|
| 459 |
Having a framework means forcing users into it. It restrains flexibility and creativity, which are the fertile soil for new ideas to grow.
|
| 460 |
|
| 461 |
-
Among the most valuable contributions to `transformers` is of course the addition of new models. Very recently, [OpenAI added GPT-OSS](https://huggingface.co/blog/welcome-openai-gpt-oss), which prompted the addition of many new features to the library in order to support [their model](https://huggingface.co/openai/gpt-oss-120b).
|
| 462 |
|
| 463 |
-
|
| 464 |
|
| 465 |
|
| 466 |
<div class="crumbs">
|
| 467 |
-
The shape of a contribution: add a model (or variant) with a small modular shard; the community and serving stacks pick it up immediately. Popularity trends (encoders/embeddings) guide where we invest.
|
|
|
|
|
|
|
| 468 |
</div>
|
| 469 |
|
| 470 |
|
| 471 |
### <a id="encoders-ftw"></a> Models popularity
|
| 472 |
|
| 473 |
-
Talking about dependencies, we can take a look at the number of downloads
|
| 474 |
|
| 475 |
<div>
|
| 476 |
<HtmlEmbed src="transformers/model-visualisation.html" />
|
| 477 |
</div>
|
| 478 |
|
| 479 |
-
As the codebase grows, with our friend
|
| 480 |
-
|
| 481 |
|
| 482 |
In that regard, we DO want to be a modular toolbox, being [minimal](#minimal-user-api) enough and well documented enough so any ML/AI developer can use `transformers` without having to think about it. We aim to reduce the cognitive load brought about by model development, not increase it.
|
| 483 |
|
| 484 |
So, how do these design choices, these "tenets" influence development of models and overall usage of transformers?
|
| 485 |
|
| 486 |
<div class="crumbs">
|
| 487 |
-
Encoders remain critical for embeddings and retrieval; maintaining them well benefits the broader ecosystem (e.g., Sentence Transformers, FAISS).
|
|
|
|
|
|
|
| 488 |
</div>
|
| 489 |
|
| 490 |
|
| 491 |
## A surgical toolbox for model development
|
| 492 |
|
|
|
|
|
|
|
| 493 |
### Attention visualisation
|
| 494 |
|
| 495 |
-
All models have the same API
|
| 496 |
|
| 497 |
One particular piece of machinery is the `attention mask`. Here you see the famous bidirectional attention pattern for the whole prefix (text + image) in PaliGemma and all Gemma2+ models, contrasting with the usual "causal-only" models.
|
| 498 |
|
| 499 |
<HtmlEmbed src="transformers/attention-visualizer.html" />
|
| 500 |
|
| 501 |
<div class="crumbs">
|
| 502 |
-
Uniform attention APIs enable cross-model diagnostics (e.g., PaliGemma prefix bidirectionality vs causal).
|
|
|
|
|
|
|
| 503 |
</div>
|
| 504 |
|
| 505 |
|
| 506 |
### Logging entire model activations
|
| 507 |
|
| 508 |
-
|
| 509 |
|
| 510 |
-
It just works with PyTorch models and is especially useful when aligning outputs with a reference implementation,
|
| 511 |
|
| 512 |

|
| 513 |
|
| 514 |
|
| 515 |
<div class="crumbs">
|
| 516 |
-
Forward interception and nested JSON logging align ports to reference implementations, reinforcing "Source of Truth." <strong>
|
|
|
|
|
|
|
| 517 |
</div>
|
| 518 |
|
| 519 |
|
| 520 |
|
| 521 |
### Cooking faster CUDA warmups
|
| 522 |
|
| 523 |
-
Having a clean _external_ API allows us to work on the [true inner workings of transformers](#code-is-product). One of
|
| 524 |
|
| 525 |
<HtmlEmbed src="transformers/warmup_demo.html" />
|
| 526 |
|
| 527 |
It's hard to overstate how much of a lifesaver that is when you're trying to load a model as fast as possible, as it's the narrowest bottleneck for your iteration speed.
|
| 528 |
|
| 529 |
<div class="crumbs">
|
| 530 |
-
Pre-allocating GPU memory removes malloc spikes (e.g., 7ร for 8B, 6ร for 32B in the referenced PR).
|
|
|
|
|
|
|
| 531 |
</div>
|
| 532 |
|
| 533 |
|
| 534 |
### Transformers-serve and continuous batching
|
| 535 |
|
| 536 |
-
Having all these models readily available
|
| 537 |
|
| 538 |
```bash
|
| 539 |
transformers serve
|
|
@@ -543,33 +589,45 @@ curl -X POST http://localhost:8000/v1/chat/completions \
|
|
| 543 |
-d '{"messages": [{"role": "system", "content": "hello"}], "temperature": 0.9, "max_tokens": 1000, "stream": true, "model": "Qwen/Qwen2.5-0.5B-Instruct"}'
|
| 544 |
```
|
| 545 |
|
| 546 |
-
|
| 547 |
|
| 548 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
|
| 550 |
<div class="crumbs">
|
| 551 |
-
OpenAI-compatible surface + continuous batching; kernels/backends slot in because the modeling API stayed stable.
|
|
|
|
|
|
|
| 552 |
</div>
|
| 553 |
|
| 554 |
|
| 555 |
## Community reusability
|
| 556 |
|
| 557 |
-
|
| 558 |
|
| 559 |
Adding a model to transformers means:
|
| 560 |
|
| 561 |
- having it immediately available to the community
|
| 562 |
-
- having it immediately usable in vLLM, [SGLang](https://huggingface.co/blog/transformers-backend-sglang), and so on without additional code. In
|
|
|
|
| 563 |
|
| 564 |
-
This cements the need
|
| 565 |
|
| 566 |
|
| 567 |
<div class="crumbs">
|
| 568 |
-
Being a good backend consumer requires a consistent public surface; modular shards and configs make that stability practical.
|
|
|
|
|
|
|
| 569 |
</div>
|
| 570 |
|
| 571 |
## What is coming next
|
| 572 |
|
| 573 |
-
The next major version of `transformers` is just around the corner (and will have another blog post to its name when it comes out
|
| 574 |
|
| 575 |
We will lean further into a modular toolbox, not a framework. You should not be forced to rewrite modeling code. Itโs better when a model can inherit from `PreTrainedModel` and opt into Tensor Parallel, `from_pretrained`, sharding, `push_to_hub`, loss plumbing, and external stacks like PEFT/TRL/SGLang/vLLM.
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
This post dissects the design philosophy that makes this possible. It's the result of a gradual evolution from our older principles, detailed on our previous [philosophy](https://huggingface.co/docs/transformers/en/philosophy) page, as well as its accompanying [blog post from 2022](https://huggingface.co/blog/transformers-design-philosophy). More recently (and I do recommend the read), we wrote a blog post about [recent upgrades to transformers](https://huggingface.co/blog/faster-transformers) with a special focus on what makes the library faster today. All of these developments were only made possible thanks to these principles.
|
| 31 |
|
| 32 |
+
We formalize and articulate the "tenets" that have been guiding our development, demonstrate how they are implemented in code, and show the measurable impact they have on the library's sustainability and growth.
|
| 33 |
|
| 34 |
For any OSS maintainer, power user, or contributor, this is the map to understanding, using, and building upon `transformers`; but not only: any project of comparable size will require you to make deep choices, not only on design and choice of abstractions, but on the very mindset of the software you are building. These tenets may or may not be applicable to your project, but they provide a glimpse on how we work that could be helpful or inspirational.
|
| 35 |
|
| 36 |
+
Conventions used throughout this post:
|
| 37 |
|
| 38 |
* [Tenets exemplified](#source-of-truth) will have their summary available on hover.
|
| 39 |
|
|
|
|
| 185 |
|
| 186 |
That gives an "effective LOC" curve: the ๐บ๐ฎ๐ถ๐ป๐๐ฒ๐ป๐ฎ๐ป๐ฐ๐ฒ ๐๐๐ฟ๐ณ๐ฎ๐ฐ๐ฒ.
|
| 187 |
|
| 188 |
+
Measured on git history, raw `modeling_*.py` grew at ~362 LOC/day before modular; counting only modular shards yields ~25 LOC/day after โ about **15ร lower**. The effective curve (blue line below) represents the **maintenance surface** today: what maintainers actually read and review.
|
| 189 |
|
| 190 |
+
<!-- Yeah, super good point that effective == maintenable -->
|
| 191 |
+
|
| 192 |
+
Less code to hand-maintain means fewer places to break. Of course LOC is not a direct measure of complexity, but they correlate in review effort and change risk.
|
| 193 |
|
| 194 |
<HtmlEmbed src="transformers/loc-growth.html" />
|
| 195 |
|
| 196 |
+
<!-- What is "Modeling LOC (included)"? The modeling code, not counting the files that have a modular counterpart? If so, perhaps we can say that the blue line (effective) is the sum of the red + green, whereas the yellow would have been the progression without modular. Also worth mentioning imo that the surface area has been essentially constant (in LOC) since modular. -->
|
| 197 |
+
|
| 198 |
+
Notice there's a sharp drop at the end of the curves, this is mostly due to us [removing support for Jax and TensorFlow](https://github.com/huggingface/transformers/commit/4df2529d79d75f44e70396df5888a32ffa02d61e#diff-60849db3e9922197854ef1cac92bf4aba08b5d7fd3fe6f3c16a3511e29e0eacc) library-wide.
|
| 199 |
|
| 200 |
+
But this was not the only effort that allowed us to reduce maintenance load.
|
| 201 |
|
| 202 |
+
We recently underwent a thoughtful refactor of the attention implementation. You've likely heard about [flash attention](https://huggingface.co/docs/text-generation-inference/en/conceptual/flash_attention) and its several variants.
|
| 203 |
|
| 204 |
+
_Attention computation_ happens at a _lower_ level of abstraction than the model itself.
|
| 205 |
|
| 206 |
+
However, we were adding specific torch operations to every model for each backend (sdpa, various flash-attention versions, flex attention) but it wasn't a [minimal user api](#minimal-user-api). Next section explains what we did.
|
| 207 |
|
| 208 |
<div class="crumbs">
|
| 209 |
+
Evidence: effective (i.e., maintenable) LOC growth drops ~15ร when counting shards instead of expanded modeling files. Less code to read, fewer places to break.
|
| 210 |
+
|
| 211 |
+
<strong>Next:</strong> how the attention interface stays standard without hiding semantics.
|
| 212 |
</div>
|
| 213 |
|
| 214 |
### <a id="attention-classes"></a> External Attention classes
|
| 215 |
|
| 216 |
+
The solution for the "attention abstraction problem" was to move to a standard [attention interface](https://huggingface.co/docs/transformers/en/attention_interface) that allows the following:
|
| 217 |
|
| 218 |
+
The naive implementation of attention, called "eager", is available by default. We use a `Callable` called `eager_attention_forward`, which can run as long as the user has PyTorch installed โย which is a requirement any way.
|
| 219 |
|
| 220 |
+
Instead of using a class interface and a class hierarchy, we just moved to a function interface. When a more complex attention implementation is needed, we use other Callables, including much faster kernel bindings when available. The decision to use a different attention implementation is based on the model configuration file we download from the Hub, and it can also be overridden by the user.
|
| 221 |
|
| 222 |
+
This is a clear example that that we prefer an interface that is [standard, but not abstract](#standardize-dont-abstract). To be completely precise, this is what the interface selection looks like in transformers code:
|
| 223 |
|
| 224 |
```python
|
| 225 |
attention_interface: Callable = eager_attention_forward
|
|
|
|
| 227 |
attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
|
| 228 |
```
|
| 229 |
|
| 230 |
+
A strength of the new attention interface is the possibility to enforce specific kwargs, which are needed by kernel providers and other dependencies. We know that kwargs are often a necessary evil that plagues tools with widespread compatibility; it is something we have aimed to reduce, and will continue to reduce in order to improve readability - with them, the current system is a [minimal user api](#minimal-user-api).
|
| 231 |
|
| 232 |
+
<!-- not fully following the transition here -->
|
| 233 |
+
|
| 234 |
+
Backend integrations sometimes require specific kwargs. We reduce that surface and document expectations; where flexibility is necessary, we plan to use `typing.Annotated` to convey shapes and invariants without constraining integrations. Such an implementation could look like this in the future:
|
| 235 |
|
| 236 |
```python
|
| 237 |
from typing import Annotated
|
|
|
|
| 241 |
|
| 242 |
|
| 243 |
<div class="crumbs">
|
| 244 |
+
Attention semantics remain in <code>eager_attention_forward</code>; faster backends are opt-in via config. We inform via types/annotations rather than enforce rigid kwargs, preserving integrations.
|
| 245 |
+
|
| 246 |
+
<strong>Next:</strong> parallel partitioning is declared as a plan, not through model surgery.
|
| 247 |
</div>
|
| 248 |
|
| 249 |
### <a id="simpler-tensor-parallelism"></a> Configurable Tensor Parallelism
|
| 250 |
|
| 251 |
If you're not familiar with the different flavours of parallelism, I recommend checking out [this blog post](https://huggingface.co/blog/accelerate-nd-parallel) first, and of course a full [dive into the ultra-scale playbook](https://huggingface.co/spaces/nanotron/ultrascale-playbook) is always recommended.
|
| 252 |
|
| 253 |
+
The essential part is that, as [the documentation states](https://huggingface.co/docs/transformers/v4.56.2/perf_train_gpu_many#tensor-parallelism), when tensors get too large to fit on a single GPU, they are sliced along a particular dimension and every slice is sent to a different GPU.
|
| 254 |
|
| 255 |
Why does it matter?
|
| 256 |
|
| 257 |
Because we want to avoid code modifications that are unrelated to the model.
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
+
We choose to place the level of abstraction higher than the device placement: a matrix multiplication - a `nn.Linear` layer - should be always expressed in the same way, regardless of how it is placed.
|
| 260 |
|
| 261 |
+
Hence, we want to touch the modeling code [minimally](#minimal-user-api), and only modify it when _architectural changes_ are involved โ not depending on the way you run it. For tensor parallelism, we simply specify a `tp_plan`:
|
| 262 |
|
| 263 |
<HtmlEmbed src="transformers/tp-plan.html" />
|
| 264 |
|
| 265 |
+
The plan is written once, saved as part of the config and passed to `.from_pretrained()`. It maps module name patterns to partitioning strategies. Strategies are resolved by the internal `ParallelInterface`, which wires to sharding implementations `ColwiseParallel`, `RowwiseParallel`, packed variants, and so on.
|
| 266 |
+
|
| 267 |
+
The alternative would be to modify classes depending on supported types of parallelism.
|
| 268 |
|
| 269 |
+
The `tp_plan` solution allows users to run the same model on a single GPU, or distribute it using multiple processes per node, e.g. 4 GPUs:
|
| 270 |
|
| 271 |
`torchrun --nproc-per-node 4 demo.py`
|
| 272 |
|
| 273 |
+
Semantics stay in the model (a Linear stays a Linear), parallelization is orthogonal and declared via strings: "colwise" splits columns of weights/bias across ranks; "rowwise" splits rows; packed variants shard fused weights; The mapping keys accept glob patterns like `layers.*.mlp.down_proj` to target repeated submodules.
|
| 274 |
|
| 275 |
<div class="crumbs">
|
| 276 |
+
Parallelization is specified in the configuration (<code>tp_plan</code>), not through edits to <code>Linear</code>s. Glob patterns target repeated blocks; modeling semantics stay intact.
|
| 277 |
+
|
| 278 |
+
<strong>Next:</strong> per-layer attention/caching schedules declared in config, not hardcoded.
|
| 279 |
</div>
|
| 280 |
|
| 281 |
### <a id="layers-attentions-caches"></a> Layers, attentions and caches
|
| 282 |
|
| 283 |
+
Following the same logic, the _nature_ of attention and per-layer caching should not be hardcoded. We should be able to specify in the configuration how each layer is implemented. Thus, we define a mapping like:
|
| 284 |
|
| 285 |
|
| 286 |
```python
|
|
|
|
| 293 |
)
|
| 294 |
```
|
| 295 |
|
| 296 |
+
and the configuration can be _explicit_ about which attention type is in which layer. See, for example, [gpt-oss](https://huggingface.co/openai/gpt-oss-120b/blob/main/config.json#L15), which alternates sliding and full attention:
|
| 297 |
|
| 298 |
```python
|
| 299 |
"layer_types": [
|
|
|
|
| 305 |
],
|
| 306 |
```
|
| 307 |
|
| 308 |
+
This is [minimal](#minimal-user-api) to implement on the user side, and allows to keep the modeling code untouched. It is also easy to tweak.
|
| 309 |
|
| 310 |
<div class="crumbs">
|
| 311 |
+
Allowed layer types are explicit; schedules (e.g., sliding/full alternation) live in config. This keeps the file readable and easy to tweak.
|
| 312 |
+
|
| 313 |
+
<strong>Next:</strong> speedups come from kernels that don't change semantics.
|
| 314 |
</div>
|
| 315 |
|
| 316 |
|
|
|
|
| 324 |
...
|
| 325 |
```
|
| 326 |
|
| 327 |
+
This also opens another contribution path: GPU specialists can contribute optimized kernels to the [Kernels Hub](https://huggingface.co/kernels-community), and have them immediately available to use in `transformers` and other libraries. You can check the [kernel community blog post](https://huggingface.co/blog/hello-hf-kernels) to learn more about it!
|
| 328 |
|
| 329 |
Even more resources have been added, like the formidable [kernel builder](https://github.com/huggingface/kernel-builder) with its connected resources to [help you build kernels with it](https://github.com/huggingface/kernel-builder/blob/main/docs/writing-kernels.md) and [with nix](https://github.com/huggingface/kernel-builder/blob/main/docs/nix.md).
|
| 330 |
|
|
|
|
| 331 |
<div class="crumbs">
|
| 332 |
+
Models define semantics; kernels define how to run them faster. Use decorations to borrow community forwards while keeping a consistent public surface.
|
| 333 |
+
|
| 334 |
+
<strong>Next:</strong> what modularity looks like across the repo.
|
| 335 |
</div>
|
| 336 |
|
| 337 |
+
## The Sate of Modular
|
| 338 |
+
|
| 339 |
+
Modular provides a form of inheritance in our codebase. Some models become standards, and model contributors have the opportunity to _define standards_ if their architectures are adopted. Pushing the boundaries of scientific knowledge can translate into the boundaries of engineering if this effort is made, and we're striving for it.
|
| 340 |
|
|
|
|
| 341 |
It's hard to conceptualize very large libraries and how their components interact with each other, regardless of your cognitive abilities for abstractions.
|
| 342 |
So I wanted to take a look at the current **state of modularity** across the repository. How many models are defined using components of others?
|
| 343 |
|
|
|
|
| 346 |
2. In this `modular` file, what models, configurations and processings are imported?
|
| 347 |
3. Recurse through the model list that way.
|
| 348 |
|
| 349 |
+
So what do we see? Llama is a basis and an influence for many models, and it shows.
|
| 350 |
Radically different architectures such as mamba have spawned their own dependency subgraph.
|
| 351 |
|
| 352 |
+
<!-- A couple of ideas here:
|
| 353 |
+
- Use screenshots to clearly show the points we make. For example, the cluster with Llama in the center, or the one about DETR/llava below.
|
| 354 |
+
- Use a link to open the viewer full-screen for better manipulation and exploration.
|
| 355 |
+
-->
|
| 356 |
+
|
| 357 |
+
(Graph reading guide: nodes are models; edges are modular imports).
|
| 358 |
|
| 359 |
<HtmlEmbed src="transformers/dependency-graph.html" />
|
| 360 |
|
| 361 |
+
In the case of VLMs, there's far too many vision-based architectures that are not yet defined as modulars of other existing archs. In other words, there is no strong reference point in terms of software for vision models.
|
| 362 |
As you can see, there is a small DETR island, a little llava pocket, and so on, but it's not comparable to the centrality observed for llama.
|
| 363 |
|
| 364 |
+
Another problem is, this visualization only shows `modular` models. Several models still do NOT have a modular file.
|
| 365 |
|
| 366 |
How do we spot them, and how do we identify modularisable models?
|
| 367 |
|
| 368 |
<div class="crumbs">
|
| 369 |
+
Llama-lineage is a hub; several VLMs remain islands โ engineering opportunity for shared parents.
|
| 370 |
+
|
| 371 |
+
<strong>Next:</strong> timeline + similarity signals to spot modularisable candidates.
|
| 372 |
</div>
|
| 373 |
|
| 374 |
|
| 375 |
### Many models, but not enough yet, are alike
|
| 376 |
|
| 377 |
+
I looked into Jaccard similarity, which we use to measure set differences, to find similarities across models. I know that code is more than a set of characters stringed together. We also tried code-embedding models that ranked candidates better in practice, but for this post we stick to the deterministic Jaccard index.
|
|
|
|
| 378 |
|
| 379 |
+
It is interesting, for our comparison, to look at _when_ we deployed the modular logic and what was its rippling effect on the library. You can check the [larger space](https://huggingface.co/spaces/Molbap/transformers-modular-refactor) to play around, but the gist is: adding modular allowed to connect more and more models to solid reference points. But we still have a lot of gaps to fill.
|
| 380 |
|
| 381 |
Zoom out below - it's full of models. You can click on a node to see its connections better, or use the text box to search for a model.
|
| 382 |
|
| 383 |
<HtmlEmbed src="transformers/model-timeline.html" />
|
| 384 |
|
| 385 |
+
<!-- screenshot would be helpful -->
|
| 386 |
+
|
| 387 |
+
If you check llava, you've seen that llava_video is a red node, connected by a red edge to llava: it's a candidate, something that we can _likely_ remodularize, [not touching the actual model](#backwards-compatibility) but being much more readable with [DRY*](#do-repeat-yourself).
|
| 388 |
|
| 389 |
<div class="crumbs">
|
| 390 |
+
Similarity metrics (Jaccard or embeddings) surface likely parents; the timeline shows consolidation after modular landed. Red nodes/edges = candidates (e.g., <code>llava_video</code> โ <code>llava</code>) for refactors that preserve behavior.
|
| 391 |
+
|
| 392 |
+
<strong>Next:</strong> concrete VLM choices that avoid leaky abstractions.
|
| 393 |
</div>
|
| 394 |
|
| 395 |
### VLM improvements, avoiding abstraction
|
| 396 |
|
| 397 |
+
We don't yet have a cookbook for common VLM patterns (image token scatter, multiโtower encoders, crossโattention bridges). This is one of the main improvement points where we can work.
|
| 398 |
|
| 399 |
For instance, we thought of abstracting away the mixing of `inputs_embeds`, the tensor fed into an LLM decoder in 95% of the existing VLMs. It would have looked like something like
|
| 400 |
|
|
|
|
| 403 |
#
|
| 404 |
```
|
| 405 |
|
| 406 |
+
But this is [abstracting away an important component of the modeling](#standardize-dont-abstract). Embedding mixin is part of the model, removing it would break it. A user opening [`modeling_qwen2.5_vl`](https://huggingface.co/collections/Qwen/qwen25-vl-6795ffac22b334a837c0f9a5) should not have to go to another file to understand how it works.
|
| 407 |
+
|
| 408 |
+
<!-- ^ should we link to the code instead? -->
|
| 409 |
|
| 410 |
What is the current state of these โabstractionsโ across the codebase?
|
| 411 |
You will see all the imports around a modeling file, here [Gemma3n](https://huggingface.co/google/gemma-3n-E4B-it).
|
|
|
|
| 462 |
|
| 463 |
But this is _within_ the modeling file, not in the `PreTrainedModel` base class. It will not move away from it, because it'd break the [self-contained logic](#one-model-one-file) of the model.
|
| 464 |
|
| 465 |
+
<!-- So the main conclusion here is that VLMs should use modular more to come up with de-facto standard modules without abstracting them away? -->
|
| 466 |
+
|
| 467 |
<div class="crumbs">
|
| 468 |
+
Keep VLM embedding mix in the modeling file (semantics), standardize safe helpers (e.g., placeholder masking), don't migrate behavior to <code>PreTrainedModel</code>.
|
| 469 |
+
|
| 470 |
+
<strong>Next:</strong> pipeline-level wins that came from PyTorch-first choices (fast processors).
|
| 471 |
</div>
|
| 472 |
|
| 473 |
|
| 474 |
### On image processing and processors
|
| 475 |
|
| 476 |
+
Deciding to become a `torch`-first library meant relieving a tremendous amount of support for `jax ` and `TensorFlow`, and it also meant that we could be more lenient into the amount of torch-dependent utilities that we were able to accept. One of these is the _fast processing_ of images. Where inputs were once minimally assumed to be ndarrays, enforcing native `torch` and `torchvision` inputs allowed us to massively improve processing speed for each model.
|
| 477 |
|
| 478 |
+
The gains in performance are immense, up to 20x speedup for most models when using compiled torchvision ops. Furthermore, it allows to run the whole pipeline solely on GPU.
|
| 479 |
|
| 480 |

|
| 481 |
<p class="figure-legend">Thanks <a href="https://huggingface.co/yonigozlan">Yoni Gozlan</a> for the great work!</p>
|
| 482 |
|
| 483 |
<div class="crumbs">
|
| 484 |
+
PyTorch-first lets processors assume torch/torchvision and run the whole pipeline on GPU; big per-model speedups.
|
| 485 |
+
|
| 486 |
+
<strong>Next:</strong> how this lowers friction for contributors and downstream users.
|
| 487 |
</div>
|
| 488 |
|
| 489 |
|
|
|
|
| 493 |
|
| 494 |
Having a framework means forcing users into it. It restrains flexibility and creativity, which are the fertile soil for new ideas to grow.
|
| 495 |
|
| 496 |
+
Among the most valuable contributions to `transformers` is of course the addition of new models. Very recently, [OpenAI added GPT-OSS](https://huggingface.co/blog/welcome-openai-gpt-oss), which prompted the addition of many new features to the library in order to support [their model](https://huggingface.co/openai/gpt-oss-120b). These additions are immediately available for other models to use.
|
| 497 |
|
| 498 |
+
Another important advantage is the ability to fine-tune and pipeline these models into many other libraries and tools. Check here on the hub how many finetunes are registered for [gpt-oss 120b](https://huggingface.co/models?other=base_model:finetune:openai/gpt-oss-120b), despite its size!
|
| 499 |
|
| 500 |
|
| 501 |
<div class="crumbs">
|
| 502 |
+
The shape of a contribution: add a model (or variant) with a small modular shard; the community and serving stacks pick it up immediately. Popularity trends (encoders/embeddings) guide where we invest.
|
| 503 |
+
|
| 504 |
+
<strong>Next:</strong> power tools enabled by a consistent API.
|
| 505 |
</div>
|
| 506 |
|
| 507 |
|
| 508 |
### <a id="encoders-ftw"></a> Models popularity
|
| 509 |
|
| 510 |
+
Talking about dependencies, we can take a look at the number of downloads as a measure of popularity. One thing we see is the prominence of encoders, despite the apparent prevalence of decoder LLMs. The reason is that encoders are used to generate embeddings, which have multiple downstream uses. Just check out [EmbeddingGemma](https://huggingface.co/blog/embeddinggemma) for a modern recap. Hence, it is vital to keep the encoders portion of the library viable, usable, fine-tune-able.
|
| 511 |
|
| 512 |
<div>
|
| 513 |
<HtmlEmbed src="transformers/model-visualisation.html" />
|
| 514 |
</div>
|
| 515 |
|
| 516 |
+
As the codebase grows, we need to maintain it in coordination with our friend [Sentence Transformers codebase](https://huggingface.co/sentence-transformers). Retrieval use-cases, smart databases, FAISS-based indexing rely on it, and thus indirectly on transformers.
|
|
|
|
| 517 |
|
| 518 |
In that regard, we DO want to be a modular toolbox, being [minimal](#minimal-user-api) enough and well documented enough so any ML/AI developer can use `transformers` without having to think about it. We aim to reduce the cognitive load brought about by model development, not increase it.
|
| 519 |
|
| 520 |
So, how do these design choices, these "tenets" influence development of models and overall usage of transformers?
|
| 521 |
|
| 522 |
<div class="crumbs">
|
| 523 |
+
Encoders remain critical for embeddings and retrieval; maintaining them well benefits the broader ecosystem (e.g., Sentence Transformers, FAISS).
|
| 524 |
+
|
| 525 |
+
<strong>Next:</strong> dev tools that leverage unified attention APIs and PyTorch-only internals.
|
| 526 |
</div>
|
| 527 |
|
| 528 |
|
| 529 |
## A surgical toolbox for model development
|
| 530 |
|
| 531 |
+
Transformers provides many tools that can help you while adding a new architecture, or help you understand the inner workings of the library.
|
| 532 |
+
|
| 533 |
### Attention visualisation
|
| 534 |
|
| 535 |
+
All models have the same internal API for attention computation, thanks to [the externalisation of attention classes](#external-attention-classes). This allows us to build cool tools to visualize the inner workings of the attention mechanism.
|
| 536 |
|
| 537 |
One particular piece of machinery is the `attention mask`. Here you see the famous bidirectional attention pattern for the whole prefix (text + image) in PaliGemma and all Gemma2+ models, contrasting with the usual "causal-only" models.
|
| 538 |
|
| 539 |
<HtmlEmbed src="transformers/attention-visualizer.html" />
|
| 540 |
|
| 541 |
<div class="crumbs">
|
| 542 |
+
Uniform attention APIs enable cross-model diagnostics (e.g., PaliGemma prefix bidirectionality vs causal).
|
| 543 |
+
|
| 544 |
+
<strong>Next:</strong> whole-model tracing for ports and regressions.
|
| 545 |
</div>
|
| 546 |
|
| 547 |
|
| 548 |
### Logging entire model activations
|
| 549 |
|
| 550 |
+
Because everything is PyTorch, we can easily [debug any model](https://huggingface.co/docs/transformers/internal/model_debugging_utils) when we want to add it to transformers. We now have a power-user tool for porting or adding models, that wraps a forward pass, intercepts every submodule call, and logs shapes, dtypes, and sample statistics of inputs/outputs to nested JSON.
|
| 551 |
|
| 552 |
+
It just works with PyTorch models and is especially useful when aligning outputs with a reference implementation, to match our [Source of Truth guideline](#source-of-truth).
|
| 553 |
|
| 554 |

|
| 555 |
|
| 556 |
|
| 557 |
<div class="crumbs">
|
| 558 |
+
Forward interception and nested JSON logging align ports to reference implementations, reinforcing "Source of Truth." <strong>
|
| 559 |
+
|
| 560 |
+
Next:</strong> CUDA warmup reduces load-time without touching modeling semantics.
|
| 561 |
</div>
|
| 562 |
|
| 563 |
|
| 564 |
|
| 565 |
### Cooking faster CUDA warmups
|
| 566 |
|
| 567 |
+
Having a clean _external_ API allows us to work on the [true inner workings of transformers](#code-is-product). One of a few recent additions was the _CUDA warmup_ via `caching_allocator_warmup`, which dramatically improved loading times by pre-allocating GPU memory to avoid malloc bottlenecks during model loading. It can achieve a 7x speedup factor for an 8B model, or 6x for a 32B one, as you can check in [the PR](https://github.com/huggingface/transformers/pull/36380)!
|
| 568 |
|
| 569 |
<HtmlEmbed src="transformers/warmup_demo.html" />
|
| 570 |
|
| 571 |
It's hard to overstate how much of a lifesaver that is when you're trying to load a model as fast as possible, as it's the narrowest bottleneck for your iteration speed.
|
| 572 |
|
| 573 |
<div class="crumbs">
|
| 574 |
+
Pre-allocating GPU memory removes malloc spikes (e.g., 7ร for 8B, 6ร for 32B in the referenced PR).
|
| 575 |
+
|
| 576 |
+
<strong>Next:</strong> consistent interfaces allow transformers-serve.
|
| 577 |
</div>
|
| 578 |
|
| 579 |
|
| 580 |
### Transformers-serve and continuous batching
|
| 581 |
|
| 582 |
+
Having all these models readily available and sharing the same interface allowed us to implement transformers-serve, a CLI tool to expose models through a standard OpenAI http API.
|
| 583 |
|
| 584 |
```bash
|
| 585 |
transformers serve
|
|
|
|
| 589 |
-d '{"messages": [{"role": "system", "content": "hello"}], "temperature": 0.9, "max_tokens": 1000, "stream": true, "model": "Qwen/Qwen2.5-0.5B-Instruct"}'
|
| 590 |
```
|
| 591 |
|
| 592 |
+
transformers-serve uses continuous batching (see [this PR](https://github.com/huggingface/transformers/pull/38085) and also [this one](https://github.com/huggingface/transformers/pull/40426)) for better GPU utilization, and is very much linked to the great work of vLLM with the `paged attention kernel` โ a futher justification of [external kernels](#community-kernels).
|
| 593 |
|
| 594 |
+
transformers-serve is not meant for user-facing production services โย tools like vLLM or SGLang are super optimized for that โ, but it's useful for several use cases:
|
| 595 |
+
- Quickly verify that your model is compatible with continuous batching and paged attention.
|
| 596 |
+
- Run ad-hoc vibe tests on any model, without worrying to deploy anything.
|
| 597 |
+
- Run evaluations efficiently, again without having to spend a lot of time engineering your infrastructure.
|
| 598 |
+
|
| 599 |
+
For model deployment, check [Inference Providers](https://huggingface.co/docs/inference-providers/en/index) or roll your solution using any of the excellent serving libraries.
|
| 600 |
|
| 601 |
<div class="crumbs">
|
| 602 |
+
OpenAI-compatible surface + continuous batching; kernels/backends slot in because the modeling API stayed stable.
|
| 603 |
+
|
| 604 |
+
<strong>Next:</strong> reuse across vLLM/SGLang relies on the same consistency.
|
| 605 |
</div>
|
| 606 |
|
| 607 |
|
| 608 |
## Community reusability
|
| 609 |
|
| 610 |
+
The transformers-serve CLI built on transformers, for sure, but the library is made first and foremost to be _reused_ at large by the open-source ecosystem.
|
| 611 |
|
| 612 |
Adding a model to transformers means:
|
| 613 |
|
| 614 |
- having it immediately available to the community
|
| 615 |
+
- having it immediately usable in vLLM, [SGLang](https://huggingface.co/blog/transformers-backend-sglang), and so on without additional code. In the case of vLLM, transformers was added as a backend to run models on vLLM, which optimizes throughput/latency on top of existing transformers architectures [as seen in this great vLLM x HF blog post.](https://blog.vllm.ai/2025/04/11/transformers-backend.html)
|
| 616 |
+
- being the reference code for implementations in MLX, llama.cpp and other libraries.
|
| 617 |
|
| 618 |
+
This further cements the need for a [consistent public surface](#consistent-public-surface): we are a backend and a reference, and there's more software than us to handle serving. At the time of writing, more effort is done in that direction. We already have compatible configs for VLMs for vLLM (say that three times fast), check [here for GLM4 video support](https://github.com/huggingface/transformers/pull/40696/files), and here for [MoE support](https://github.com/huggingface/transformers/pull/40132), for instance.
|
| 619 |
|
| 620 |
|
| 621 |
<div class="crumbs">
|
| 622 |
+
Being a good backend consumer requires a consistent public surface; modular shards and configs make that stability practical.
|
| 623 |
+
|
| 624 |
+
<strong>Next:</strong> what changes in v5 without breaking the promise of visible semantics.
|
| 625 |
</div>
|
| 626 |
|
| 627 |
## What is coming next
|
| 628 |
|
| 629 |
+
The next major version of `transformers` is just around the corner (and will have another blog post to its name when it comes out). When v5 is released, we aim to keep [backwards compatibility](#backwards-compatibility) as solid as possible. The changes we make now are in service of that goal.
|
| 630 |
|
| 631 |
We will lean further into a modular toolbox, not a framework. You should not be forced to rewrite modeling code. Itโs better when a model can inherit from `PreTrainedModel` and opt into Tensor Parallel, `from_pretrained`, sharding, `push_to_hub`, loss plumbing, and external stacks like PEFT/TRL/SGLang/vLLM.
|
| 632 |
+
|
| 633 |
+
<!-- Maybe end with some statement that shows lots of excitement -->
|