SigLIP2
SigLIP2 is a dual-tower model that scores an image against text prompts with an independent sigmoid per class, instead of a shared softmax over a fixed label set. LibreYOLO supports it for zero-shot classification and image/text embedding, with no training step.
- Tasks
- classify, embed
- Sizes
- Install
pip install libreyolo- Support tier
- Sibling tier, since v. A separate product surface with its own factory and contract.
- Licenses
- Code MIT, weights Apache-2.0. Commercial use
Install
SigLIP2 needs its own extra, which pulls in the SentencePiece package its multilingual tokenizer uses.
pip install "libreyolo[siglip2]"Predict
Weights download from Hugging Face on first use and are cached locally.
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreSigLIP2b16-cls.pt")model.set_classes(["a forklift", "an empty aisle", "a spill"])result = model(SAMPLE_IMAGE, save=True) print(model.names[result.probs.top1], float(result.probs.top1conf))# With no set_classes() call, CLI predict uses the 1,000 ImageNet# class names the model loads with by default.libreyolo predict model=LibreSigLIP2b16-cls.pt source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg save=Truefrom libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreSigLIP2b16-cls.pt")model.set_classes(["a dog", "a cat", "outdoors"], multi_label=True)r = model(SAMPLE_IMAGE) # Independent per-class probabilities: more than one, or none, can# score high at once. Softmax (the default) instead normalizes them# into a single-label distribution, matching LibreCLIP's behavior.for i, name in model.names.items(): print(name, float(r.probs.data[i]))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreSigLIP2b16-cls.pt", task="embed")image_embed = model(SAMPLE_IMAGE).embeddings.datatext_embed = model.embed_text("a photo of a forklift") # Both are L2-normalized, so a plain dot product is cosine similarity.similarity = (image_embed @ text_embed.T).item()set_classes() is the one primitive that makes this an open-vocabulary classifier: it renders each label into every prompt template, encodes and averages the results, and caches the resulting [K, D] matrix as the classifier head, so it is not recomputed per image. Call it again to change classes at any time. With no call, LibreSigLIP2 loads with the 1,000 ImageNet-1k class names already set.
SigLIP scores each class independently: logit = scale * (image . text) + bias. By default that logit set is still passed through a softmax, giving a single-label distribution that matches LibreCLIP's top1/top5 behavior. Passing multi_label=True to set_classes() (or at construction) switches to independent sigmoid probabilities instead, so more than one class, or none, can score high on the same image. The tokenizer is a multilingual SentencePiece model (Gemma vocabulary), so class names in languages other than English work the same way.
With task="embed", prediction returns one L2-normalized image vector per input instead of class probabilities, and embed_text() returns normalized text rows in the same vector space, so a plain dot product between them is cosine similarity. iou has no effect on either task; there is no NMS step. See prediction for sources, streaming and result handling.
Validate
val() reads the class-folder names under an ImageFolder train/ split, calls set_classes() with them, then measures zero-shot top-1 and top-5 accuracy under softmax scoring. Accuracy depends on how the class names read as prompts, not on any weight update, since there is nothing to train. Validation only covers task="classify"; task="embed" has no dataset validator.
from libreyolo import LibreYOLO model = LibreYOLO("LibreSigLIP2b16-cls.pt") # data is an ImageFolder root with a train/ split; its folder names# become the zero-shot class prompts for this run.metrics = model.val(data="imagenette160") print(metrics["metrics/accuracy_top1"])print(metrics["metrics/accuracy_top5"])libreyolo val model=LibreSigLIP2b16-cls.pt data=imagenette160Export
| Task | ONNX | TorchScript | ExecuTorch | TensorRT | OpenVINO | Paddle | MNN | RKNN | ncnn | TFLite | CoreML | Core AI |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| classify | classify to ONNX: supported. | classify to TorchScript: supported. | classify to ExecuTorch: supported. | classify to TensorRT: supported. | classify to OpenVINO: supported. | classify to Paddle: not supported | classify to MNN: not supported | classify to RKNN: not supported | classify to ncnn: not supported | classify to TFLite: supported. | classify to CoreML: not supported | classify to Core AI: supported. |
| embed | embed to ONNX: supported. | embed to TorchScript: supported. | embed to ExecuTorch: supported. | embed to TensorRT: supported. | embed to OpenVINO: supported. | embed to Paddle: not supported | embed to MNN: not supported | embed to RKNN: not supported | embed to ncnn: not supported | embed to TFLite: supported. | embed to CoreML: not supported | embed to Core AI: not supported |
Export bakes the model's current state into a fixed graph. For task="classify", whatever labels set_classes() last set, and the resolution at export time, are baked into a final linear layer with the learned scale and bias, so the exported graph is an ordinary [B, K] image classifier with no text tower and no tokenizer; export again after changing the classes or the size. Exporting in multi_label=True mode is not implemented; set it back to False first. task="embed" export traces the image tower alone. Both need ONNX opset 14 or higher, which the exporter sets by default.
from libreyolo import LibreYOLO model = LibreYOLO("LibreSigLIP2b16-cls.pt")model.set_classes(["a forklift", "an empty aisle", "a spill"])model.export(format="onnx") # The current set_classes() labels and the input resolution are baked# into the graph. Re-export after changing either one. multi_label# must be False (the default) at export time.# No set_classes() call here, so this bakes in the default 1,000# ImageNet classes the model loads with.libreyolo export model=LibreSigLIP2b16-cls.pt format=onnxfrom libreyolo import LibreYOLO # task="embed" traces the image tower alone; no classes needed.model = LibreYOLO("LibreSigLIP2b16-cls.pt", task="embed")model.export(format="onnx")Checkpoints
Every published weight file for this family. Both are converted from Google's Apache-2.0 siglip2-base-patch16-256 and siglip2-so400m-patch14-384 checkpoints, not from any COCO training run.
| File | Input (px) | Weights license |
|---|---|---|
| classify | ||
| LibreSigLIP2b16-cls.pt | apache-2.0 | |
| LibreSigLIP2so400m-cls.pt | apache-2.0 | |
Every file above exists in the LibreYOLO org today and downloads on first use.
Licensing
Check the license on the Hugging Face repository of the specific weights you download. Every checkpoint in the LibreYOLO org carries one, and they are not always the same across a family. That repository is the authoritative source; the summary below describes what applied when this page was last verified.
This is a description of the licenses involved, not legal advice. If the answer matters commercially, read the licenses yourself and take your own counsel.
- Original work
- SigLIP 2, Google DeepMind
- Upstream license
- Apache-2.0
- Upstream source
- github.com/huggingface/transformers
- LibreYOLO code
- MIT
- Weights
- Apache-2.0, republished at huggingface.co/LibreYOLO
- Interpretation
- Apache-2.0 is a permissive license, so these weights can be used in commercial and closed-source products. It asks you to keep its license text and attribution notices with any copy of the weights you redistribute, and it grants a patent license. It places no obligation on your own application code. LibreSigLIP2's image and text towers are a clean-room re-implementation structured to match Hugging Face Transformers' SigLIP reference implementation, built without transformers as a runtime dependency; the multilingual SentencePiece tokenizer (Gemma vocabulary) is shipped verbatim from the Apache-2.0 google/siglip2-* release. The shipped checkpoints (b16, so400m) are converted from Google's Apache-2.0 siglip2-base-patch16-256 and siglip2-so400m-patch14-384 weights, a metadata wrap with the learned parameters unchanged. Training is not offered for this family: LibreSigLIP2 is zero-shot, and set_classes() replaces the fine-tuning step a trained classifier would otherwise need.
Citation
@misc{tschannen2025siglip2multilingualvisionlanguage,
title={SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features},
author={Michael Tschannen and Alexey Gritsenko and Xiao Wang and Muhammad Ferjad Naeem and Ibrahim Alabdulmohsin and Nikhil Parthasarathy and Talfan Evans and Lucas Beyer and Ye Xia and Basil Mustafa and Olivier Hénaff and Jeremiah Harmsen and Andreas Steiner and Xiaohua Zhai},
year={2025},
eprint={2502.14786},
archivePrefix={arXiv},
primaryClass={cs.CV},
url={https://arxiv.org/abs/2502.14786},
}Copied from the authors' citation block at huggingface.co/google/siglip2-base-patch16-256#bibtex-entry-and-citation-info.