Views
No views yet
siglip_classifier_head.pt — trained state_dict for CrossAttentionHead (image_dim=1152, text_dim=384, proj_dim=256, num_heads=4, hidden_dims=(256, 128)).siglip_embeddings.pt — cached SigLIP image / MiniLM text embeddings used during training (for reproducing splits/evaluation only; not required for inference).text→image attention head lets the question query the document representation,
and a symmetric image→text head lets the document representation query the question, each
followed by a residual connection and LayerNorm (transformer-block style). The two attended
vectors are concatenated (512-d) and passed through a small MLP classifier.1import torch
2from torch import nn
3
4class CrossAttentionHead(nn.Module):
5 def __init__(self, image_dim=1152, text_dim=384, proj_dim=256, num_heads=4, hidden_dims=(256, 128)):
6 super().__init__()
7 self.image_proj = nn.Linear(image_dim, proj_dim)
8 self.text_proj = nn.Linear(text_dim, proj_dim)
9 self.text_to_image_attn = nn.MultiheadAttention(proj_dim, num_heads, batch_first=True)
10 self.image_to_text_attn = nn.MultiheadAttention(proj_dim, num_heads, batch_first=True)
11 self.norm_text = nn.LayerNorm(proj_dim)
12 self.norm_image = nn.LayerNorm(proj_dim)
13 h1, h2 = hidden_dims
14 self.classifier = nn.Sequential(
15 nn.Linear(proj_dim * 2, h1),
16 nn.ReLU(),
17 nn.Linear(h1, h2),
18 nn.ReLU(),
19 nn.Linear(h2, 1),
20 )
21
22 def forward(self, image_embed, text_embed):
23 img = self.image_proj(image_embed).unsqueeze(1)
24 txt = self.text_proj(text_embed).unsqueeze(1)
25
26 text_attended, _ = self.text_to_image_attn(query=txt, key=img, value=img)
27 image_attended, _ = self.image_to_text_attn(query=img, key=txt, value=txt)
28
29 text_fused = self.norm_text(txt + text_attended).squeeze(1)
30 image_fused = self.norm_image(img + image_attended).squeeze(1)
31
32 fused = torch.cat([text_fused, image_fused], dim=-1)
33 return self.classifier(fused).squeeze(-1)1import torch
2from huggingface_hub import hf_hub_download
3
4ckpt = hf_hub_download("giacolees/siglip-doc-understanding-classifier", "siglip_classifier_head.pt")
5head = CrossAttentionHead()
6head.load_state_dict(torch.load(ckpt, map_location="cpu"))
7head.eval()
8
9# image_embed: SigLIP pooled image features (1152-d), via
10# AutoModel.from_pretrained("google/siglip-so400m-patch14-384").get_image_features(...)
11# text_embed: MiniLM sentence embedding (384-d), via
12# SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2").encode(question)
13with torch.no_grad():
14 logit = head(image_embed.unsqueeze(0), text_embed.unsqueeze(0))
15 prob_unanswerable = torch.sigmoid(logit).item()google/siglip-so400m-patch14-384sentence-transformers/all-MiniLM-L6-v2| Fusion | Acc | Prec | Rec | F1 | MCC | nlp_entity F1 | element F1 | layout F1 |
|---|---|---|---|---|---|---|---|---|
| Concat (superseded) | 0.702 | 0.711 | 0.678 | 0.695 | 0.404 | 0.738 | 0.509 | 0.336 |
| Cross-attention (this checkpoint) | 0.823 | 0.828 | 0.815 | 0.821 | 0.645 | 0.865 | 0.693 | 0.318 |
multimodalDocumentUnderstanding benchmark
(unanswerable question detection from document images).