转换脚本如下:
import torch
from transformers import AutoModel, AutoTokenizer
import torch.nn as nn
class ImageEmbedder(nn.Module):
def __init__(self, model):
super().__init__()
self.model = model
def forward(self, pixel_values):
out = self.model.get_image_features(pixel_values=pixel_values)
# 兼容不同 transformers 版本
if hasattr(out, "pooler_output"):
return out.pooler_output
return out
# 加载完整模型
full_model = AutoModel.from_pretrained("./my_local_siglip").eval()
image_embedder = ImageEmbedder(full_model)
dummy = torch.randn(1, 3, 512, 512)
with torch.no_grad():
torch.onnx.export(
image_embedder, dummy, "siglip_image_embed.onnx",
input_names=["pixel_values"], output_names=["image_embeds"],
opset_version=17, dynamic_axes=None,
)
class TextEmbedder(nn.Module):
def __init__(self, model):
super().__init__()
self.model = model
def forward(self, input_ids):
out = self.model.get_text_features(input_ids=input_ids)
# 兼容不同 transformers 版本
if hasattr(out, "pooler_output"):
return out.pooler_output
return out
text_embedder = TextEmbedder(full_model)
dummy_ids = torch.randint(0, 32000, (1, 64)) # 或用 tokenizer 生成
with torch.no_grad():
torch.onnx.export(
text_embedder, dummy_ids, "siglip_text_embed.onnx",
input_names=["input_ids"], output_names=["text_embeds"],
opset_version=17, dynamic_axes=None,
)这里有两个坑,会导致转换后输出不对,
第一个是,直接使用底层的vison tower或者text tower。
vision_model = model.vision_model
text_model = model.text_model
由于导出的是 model.vision_model 和 model.text_model。
根据 Hugging Face 文档,SiglipVisionModel 是“without any head or projection on top”的纯塔结构。
直接调用它得到的是 last_hidden_state,而不是用于比对的嵌入。
真正的图文嵌入需要经过 get_image_features / get_text_features 内部的处理:先取 pooler_output,
再经过 visual_projection 或 text_projection 映射到统一维度。
第二个坑是:
直接使用get_image_features、get_text_features 的结果,它输出的是一个class。
class ImageEmbedder(nn.Module):
def __init__(self, model):
super().__init__()
self.model = model
def forward(self, pixel_values):
return self.model.get_image_features(pixel_values=pixel_values)
一个很可能的原因是:你用的 transformers 版本中,
get_image_features / get_text_features 的返回值不再是纯张量,
而是 BaseModelOutputWithPooling 对象。
如果你在 wrapper 的 forward 里直接 return self.model.get_image_features(...),torch.onnx.export
追踪到的可能并不是对象内部的 pooler_output,而是某个底层输出,或者导出器对返回对象的处理方式与预期不符。
导出后:
2026/10/23 11:49 205,431 siglip_image_embed.onnx
2026/10/23 11:49 1,266,978,816 siglip_image_embed.onnx.data
2026/10/23 11:49 211,534 siglip_text_embed.onnx
2026/10/23 11:49 2,262,675,968 siglip_text_embed.onnx.data
权重单独保存在一个data文件中。