- 人工智能
- 大模型
- 机器学习
- 深度学习
- 本地部署
- 模型推理服务
【免费下载链接】candle
Minimalist ML framework for Rust
SigLIP 是 Google 提出的多模态图文理解模型,它用基于 Sigmoid 的对比损失替代 CLIP 的 Softmax 损失,在图文匹配、零样本分类等任务上取得更优的效果。本仓库中的 siglip 示例 以完整可运行的 Rust 代码实现了 SigLIP 的推理管线,覆盖模型下载、图像预处理、文本编码、图文特征对齐与概率输出。读完本文,你将掌握在 Candle 上运行 SigLIP 图文匹配的全部命令、每个命令行参数的含义,以及从 模型实现源码 到示例主程序 main.rs 的完整调用链。
SigLIP 是什么:基于 Sigmoid 损失的 CLIP 改进
原文档开门见山地给出了 SigLIP 的定义:SigLIP 是一个多模态文本-视觉模型,它通过基于 Sigmoid 的损失改进了 CLIP(参考模型为google/siglip-base-patch16-224)。这里的核心差异在于训练目标:
- CLIP使用 Softmax 形式的对比损失,在批内样本之间做归一化,损失与批大小、批内负样本的构成强相关;
- SigLIP将图文匹配建模为逐对(pairwise)的二分类问题,对每一对「图像-文本」组合独立计算 Sigmoid 二元交叉熵损失。这一改动让训练对批大小的敏感度降低,在更大的批规模与更少的训练轮次下即可收敛到更好的效果,这也是 SigLIP 名称(SigmoidLossIntoPre-training)的由来。
从本仓库的模型实现看,SigLIP 由两条主干组成:一条处理图像的Vision Transformer,一条处理文本的Text Transformer,两者在特征空间中通过可学习的logit_scale与logit_bias对齐,最终输出「每张图像 × 每段文本」的相似度 logits。下文将结合源码逐层拆解。
快速运行:一条命令完成图文匹配推理
原文档给出了示例的运行命令(在仓库根目录下执行):
$ cargo run --features cuda -r --example siglip参数含义拆解如下:
--features cuda:启用 CUDA 特性,让模型在 GPU 上运行(对应 Cargo.toml 中的cudafeature,它会连带启用candle/cuda、candle-nn/cuda、candle-transformers/cuda);-r:release 模式编译运行,保证推理性能;--example siglip:指定运行candle-examples包中的siglip示例。
首次运行时,程序会通过 hub.rs 封装的hf-hub客户端自动从 HuggingFace 仓库下载model.safetensors(模型权重)、config.json(模型配置)与tokenizer.json(分词器)三个文件,下载进度会实时打印到 stderr,文件缓存在本地后再次运行将直接命中缓存,不再重复下载。
运行成功后,原文档记录了如下典型输出(默认测试图像与默认文本序列的结果):
softmax_image_vec: [2.1912122e-14, 2.3624872e-14, 1.0, 1.0, 2.4787932e-8, 3.2784535e-12] Results for image: candle-examples/examples/stable-diffusion/assets/stable-diffusion-xl.jpg Probability: 0.0000% Text: a cycling race Probability: 0.0000% Text: a photo of two cats Probability: 100.0000% Text: a robot holding a candle Results for image: candle-examples/examples/yolo-v8/assets/bike.jpg Probability: 100.0000% Text: a cycling race Probability: 0.0000% Text: a photo of two cats Probability: 0.0000% Text: a robot holding a candle示例默认使用两张测试图像:
- stable-diffusion-xl.jpg(768×768):画面主体是一个复古工业风格的机器人,手持点燃的蜡烛,因此与文本
a robot holding a candle的匹配概率高达 100%; - bike.jpg(800×556):一场户外公路自行车赛事场景,多名车手正在骑行,因此与
a cycling race的匹配概率为 100%。
输出结构值得解读:softmax_image_vec是 6 个概率值,恰好是「2 张图像 × 3 段文本」的展开结果。程序随后按图像分组,每张图像内输出 3 段文本各自的匹配概率(见 main.rs 中的probability_per_image分组逻辑)。
命令行参数完全指南
示例基于clap实现命令行解析(见 main.rs 中的Args结构体),全部参数及其作用如下:
| 参数 | 类型/默认值 | 说明 |
|---|---|---|
--model <PATH> | Option | 本地模型权重文件路径;不传则自动从 HF Hub 下载model.safetensors |
--config <PATH> | Option | 本地config.json路径;不传则自动下载 |
--hf-repo <REPO> | Option | 自定义 HuggingFace 模型仓库 ID,覆盖--which的默认映射 |
--which <NAME> | v1-base-patch16-224 | 选择预定义模型规格(详见下表) |
--tokenizer <PATH> | Option | 本地tokenizer.json路径;不传则自动下载 |
--images <IMG,...> | 两个默认图像 | 输入图像路径列表,逗号分隔(use_value_delimiter = true) |
--sequences <TXT,...> | 三段默认文本 | 候选文本序列列表,逗号分隔 |
--image-size <N> | 由 config 决定 | 图像缩放边长;不传则使用config.vision_config.image_size |
--cpu | false | 强制使用 CPU 设备(覆盖设备自动选择逻辑) |
--which参数支持以下枚举值,它们同时决定了默认的 HF 仓库(见 main.rs 中的Which枚举与仓库映射):
--which取值 | 默认 HF 仓库 |
|---|---|
v1-base-patch16-224 | google/siglip-base-patch16-224 |
v2-base-patch16-224 | google/siglip2-base-patch16-224 |
v2-base-patch16-256 | google/siglip2-base-patch16-256 |
v2-base-patch16-384 | google/siglip2-base-patch16-384 |
v2-base-patch16-512 | google/siglip2-base-patch16-512 |
v2-large-patch16-256 | google/siglip2-large-patch16-256 |
v2-large-patch16-384 | google/siglip2-large-patch16-384 |
v2-large-patch16-512 | google/siglip2-large-patch16-512 |
从源码结构看,v2-*系列对应新一代的 SigLIP 2 模型,并且按输入分辨率(224/256/384/512)细分——分辨率越高,视觉分支切分的 patch 越多,匹配精度与计算量同步上升,你可以根据硬件条件权衡选择。
模型与权重获取:HF Hub 下载逻辑
当--model、--config、--tokenizer均未指定时,示例走自动化下载路径(见 main.rs):
- 创建
candle_examples::hub::Api,绑定到--hf-repo或--which对应的仓库; - 依次调用
api.get("model.safetensors")、api.get("config.json")、api.get("tokenizer.json"); - 下载后的文件由
hf-hub缓存管理。
其中 hub.rs 封装了阻塞式同步客户端HFClientSync:Repo::get会先尝试local_files_only(true)命中本地缓存,仅在缓存缺失(LocalEntryNotFound)时才真正发起网络下载,并将下载进度按百分比打印到 stderr(终端下用\r原地重绘,重定向时按 10% 一行输出)。模型加载环节使用VarBuilder::from_mmaped_safetensors对权重文件做内存映射,以DType::F32直接构建模型,避免将整个权重一次性拷入内存。
图像预处理管线
示例的图像预处理集中在 main.rs 的load_image函数中,流程清晰:
- 用
imagecrate 打开并解码图片; resize_to_fill(width, height, FilterType::Triangle)将图像等比缩放并填充到image_size × image_size(image_size默认取config.vision_config.image_size,如 base-224 模型为 224);- 转换为 RGB8 原始字节,构造成
(H, W, 3)的张量; permute((2, 0, 1))将布局调整为(C, H, W)通道优先,符合视觉 Transformer 的输入约定;to_dtype(DType::F32)后执行affine(2. / 255., -1.),把像素值从[0, 255]线性映射到[-1, 1],完成与训练一致的标准化。
多张图像通过load_images逐张预处理后,用Tensor::stack(&images, 0)在第 0 维堆叠成批,再to_device(&device)迁移到目标设备。因此你可以通过--images一次传入多张图片,批处理推理。
文本编码与填充
文本侧的处理见 main.rs 的tokenize_sequences函数:
- 用
tokenizers::Tokenizer对每段文本执行encode(seq, true)(true表示追加特殊 token); - 取
encoding.get_ids()得到 token id 序列; - 以
config.text_config.max_position_embeddings为目标长度做右填充:不足部分用pad_token_id补齐,使批内所有文本等长,便于矩阵化输入; - 构造
(batch, seq_len)的input_ids张量送入模型。
这一设计对应源码 siglip.rs 中TextConfig的两个关键默认值:max_position_embeddings = 64(base 模型的最长序列,文本侧取编码后 token 数直接填满到 64),pad_token_id = 1。这也解释了为什么示例中的三段短文本都会被补长到相同长度。
源码视角:SigLIP 模型结构
模型完整实现位于 siglip.rs,值得关注的构件如下。
双流配置结构
Config由TextConfig与VisionConfig组成,二者通过serde从 HF 的config.json反序列化,且每个字段都带默认值兜底(缺字段时自动回退)。base-224 的默认配置为:
- TextConfig:
hidden_size = 768、intermediate_size = 3072、num_hidden_layers = 12、num_attention_heads = 12、vocab_size = 32000、max_position_embeddings = 64、layer_norm_eps = 1e-6、激活函数为GeluPytorchTanh,token id 方面pad_token_id = 1、bos_token_id = 49406、eos_token_id = 49407; - VisionConfig:
patch_size = 16、hidden_size = 768、intermediate_size = 3072、num_hidden_layers = 12、num_attention_heads = 12、num_channels = 3、image_size = 224。
代码中还提供了Config::base_patch16_224()与VisionConfig::paligemma_3b_224/448/896()等内置构造器,其中num_patches()即(image_size / patch_size)^2,可见 SigLIP 的视觉编码器结构同时被 PaliGemma 等多模态模型复用。
视觉分支:Vision Transformer + MHA Pooling Head
视觉分支是一个无[CLS]token 的标准 ViT,包含四段(见VisionTransformer):
- Patch Embedding:用
Conv2d(stride =patch_size,如 16)将3×224×224的图像切成14×14 = 196个 patch,映射到 768 维; - 位置编码:
VisionEmbeddings::forward中,当输入分辨率与训练分辨率一致时直接复用预训练位置编码;不一致时通过interpolate2d做二维插值后再flatten_from(2)摊平为序列,这为--image-size传入非标准尺寸(如 256/384/512)提供了支持; - Transformer Encoder:12 层标准 EncoderLayer(LayerNorm → 多头自注意力 → 残差 → LayerNorm → MLP → 残差),MLP 展开比为 4(768 → 3072 → 768),激活为
GeluPytorchTanh; - MultiheadAttentionPoolingHead:视觉侧不使用
[CLS]token,而是引入一个可学习的probe向量作为 query,对全部 patch 特征做一次交叉注意力池化(attention.forward(&probe, xs, xs)),随后过 LayerNorm 与 MLP 并接残差,取序列第 0 位作为整图的视觉特征。VisionModel::new(cfg, use_head=true, vb)中的use_head开关表明该池化头可按需启用。
文本分支:Text Transformer
TextTransformer依次为 token embedding + 可学习位置 embedding(position_ids预生成 0..max 的 arange)、12 层共享的EncoderLayer、final_layer_norm与一个head线性层。值得注意的实现细节:forward中取的是最后一个位置i((.., seq_len - 1, ..))的隐藏状态再过 head——与视觉侧的池化不同,文本侧以序列末位 token 的表示作为整段文本的特征向量。
图文对齐:L2 归一化 × logit_scale + logit_bias
Model::forward(见 siglip.rs 的Model实现)把两条分支串起来:
image_features = vision_model(pixel_values) # [n_images, 768] text_features = text_model(input_ids) # [n_texts, 768] image_features = L2 归一化(image_features) text_features = L2 归一化(text_features) logits_per_text = text_features @ image_features^T # [n_texts, n_images] logits_per_text = logits_per_text * exp(logit_scale) + logit_bias logits_per_image = logits_per_text^T # [n_images, n_texts]其中logit_scale、logit_bias都是[1]形状的可学习标量,随权重一起加载(对应vb.get(&[1], "logit_scale"))。logits_per_image[i][j]即第 i 张图像与第 j 段文本的匹配得分。
回到示例主程序 main.rs:拿到logits_per_image后,沿第 1 维(文本方向)做softmax,得到每张图像内各候选文本的归一化概率softmax_image_vec;再乘以 100 得到百分比概率并按图像分组打印。由于是批内归一化,你可以通过--sequences自由替换候选文本集合,模型会在给定候选中给出相对最优的匹配——这正是零样本图文检索/分类的核心用法。
多分辨率推理与设备选择
除默认的v1-base-patch16-224外,--which还提供了 224/256/384/512 四种分辨率的 v2 规格。分辨率越大,视觉分支 patch 数越多(num_patches = (image_size / 16)^2),计算量按平方增长。对于 224 以外的尺寸,源码会走位置编码插值路径,因此无需更换权重即可直接尝试更高分辨率。
设备选择方面,示例通过candle_examples::device(args.cpu)决定运行设备:
- 默认优先 CUDA(编译时启用
--features cuda,对应 Cargo.toml 的cudafeature 及其对candle、candle-nn、candle-transformers的级联启用); - 传入
--cpu则强制 CPU; - 在 macOS 上可改用
--features metal启用 Metal 后端,在 Intel 平台可启用--features mkl或--features accelerate接入 BLAS 加速(见 Cargo.toml 的 feature 定义)。
小结
从一条cargo run命令出发,本文完整覆盖了 Candle 上 SigLIP 图文匹配的端到端链路:基于 Sigmoid 损失的多模态模型背景、HF Hub 权重自动获取、[-1, 1]标准化与resize_to_fill图像预处理、按max_position_embeddings填充的文本编码,以及「双塔编码 → L2 归一化 → logit_scale/logit_bias → 按图 softmax」的相似度计算流程。如果你需要把 SigLIP 集成进自己的检索或零样本分类系统,直接参考 示例主程序 的load_image、tokenize_sequences与model.forward三段逻辑即可快速迁移,底层实现细节可在 siglip.rs 中进一步研读。
- 人工智能
- 大模型
- 机器学习
- 深度学习
- 本地部署
- 模型推理服务
【免费下载链接】candle
Minimalist ML framework for Rust
相关推荐
Candle 中的 CLIP 多模态推理实战:用 Rust 实现图像与文本的对比学习匹配
Candle 中的 CLIP 多模态推理实战:用 Rust 实现图像与文本的对比学习匹配 导读 本文围绕 candle 仓库中的 CLIP(Contrastiv
人工智能大模型机器学习深度学习本地部署模型推理服务5分钟上手Cloudflare Workers Next.js SaaS Template:从安装到部署的完整教程
5分钟上手Cloudflare Workers Next.js SaaS Template:从安装到部署的完整教程 Cloudflare Workers Nex
SigLIP-SO400M多模态模型实战指南:从入门到精通
SigLIP SO400M多模态模型实战指南:从入门到精通 SigLIP SO400M是Google推出的先进视觉 语言多模态模型,在零样本图像分类任务中表现卓
大模型深度学习计算机视觉多模态
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考