Zero-Shot Image Classification
Transformers
Safetensors
tipsv2
feature-extraction
vision
image-text
contrastive-learning
zero-shot
custom_code
Instructions to use google/tipsv1-l14 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use google/tipsv1-l14 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-image-classification", model="google/tipsv1-l14", trust_remote_code=True) pipe( "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/parrots.png", candidate_labels=["animals", "humans", "landscape"], )# Load model directly from transformers import AutoProcessor, AutoModel processor = AutoProcessor.from_pretrained("google/tipsv1-l14", trust_remote_code=True) model = AutoModel.from_pretrained("google/tipsv1-l14", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Commit ·
05c1dd4
0
Parent(s):
tips v1 l14
Browse files- .gitattributes +35 -0
- README.md +113 -0
- config.json +85 -0
- configuration_tips.py +45 -0
- image_encoder.py +1002 -0
- model.safetensors +3 -0
- modeling_tips.py +141 -0
- processor_config.json +16 -0
- text_encoder.py +342 -0
- tokenizer.model +3 -0
- tokenizer_config.json +12 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
tags:
|
| 4 |
+
- vision
|
| 5 |
+
- image-text
|
| 6 |
+
- contrastive-learning
|
| 7 |
+
- zero-shot
|
| 8 |
+
- feature-extraction
|
| 9 |
+
- arxiv:2410.16512
|
| 10 |
+
library_name: transformers
|
| 11 |
+
pipeline_tag: zero-shot-image-classification
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
# TIPS — L/14 (v1)
|
| 15 |
+
|
| 16 |
+
TIPS (Text-Image Pre-training with Spatial awareness, ICLR 2025) is a family of contrastive vision-language models that produce spatially rich image features aligned with text embeddings. This is the original (v1) L/14 release with 304M vision params and 184M text params, converted from the [official checkpoints](https://github.com/google-deepmind/tips).
|
| 17 |
+
|
| 18 |
+
| Variant | Vision params | Text params | Embed dim | Resolution |
|
| 19 |
+
|---------|---------------|-------------|-----------|------------|
|
| 20 |
+
| [S/14](https://huggingface.co/google/tipsv1-s14) | 22M | 34M | 384 | 448 |
|
| 21 |
+
| [B/14](https://huggingface.co/google/tipsv1-b14) | 86M | 110M | 768 | 448 |
|
| 22 |
+
| [L/14](https://huggingface.co/google/tipsv1-l14) | 304M | 184M | 1024 | 448 |
|
| 23 |
+
| [So400m/14](https://huggingface.co/google/tipsv1-so400m14) | 413M | 448M | 1152 | 448 |
|
| 24 |
+
| [g/14](https://huggingface.co/google/tipsv1-g14) | 1.1B | 389M | 1536 | 448 |
|
| 25 |
+
| [g/14 low-res](https://huggingface.co/google/tipsv1-g14-lowres) | 1.1B | 389M | 1536 | 224 |
|
| 26 |
+
|
| 27 |
+
## Usage
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
pip install transformers torch torchvision sentencepiece scikit-learn requests
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
### Load the model
|
| 34 |
+
|
| 35 |
+
```python
|
| 36 |
+
from transformers import AutoModel
|
| 37 |
+
|
| 38 |
+
model = AutoModel.from_pretrained("google/tipsv1-l14", trust_remote_code=True)
|
| 39 |
+
model.eval()
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
### Encode images
|
| 43 |
+
|
| 44 |
+
Images should be tensors in `[0, 1]` range (just `ToTensor()`, no ImageNet normalization).
|
| 45 |
+
|
| 46 |
+
```python
|
| 47 |
+
import requests
|
| 48 |
+
from PIL import Image
|
| 49 |
+
from torchvision import transforms
|
| 50 |
+
|
| 51 |
+
url = "https://huggingface.co/spaces/google/TIPSv2/resolve/main/examples/zeroseg/pascal_context_00049_image.png"
|
| 52 |
+
image = Image.open(requests.get(url, stream=True).raw).convert("RGB")
|
| 53 |
+
transform = transforms.Compose([transforms.Resize((448, 448)), transforms.ToTensor()])
|
| 54 |
+
pixel_values = transform(image).unsqueeze(0)
|
| 55 |
+
|
| 56 |
+
out = model.encode_image(pixel_values)
|
| 57 |
+
print(out.cls_token.shape) # (1, 1, 1024) — global image embedding
|
| 58 |
+
print(out.patch_tokens.shape) # (1, 1024, 1024) — per-patch spatial features
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
The second CLS token (`out.register_tokens`) was trained on synthetic captions; the first (`out.cls_token`) on web alt-text, and is the one aligned with the text tower.
|
| 62 |
+
|
| 63 |
+
### Encode text
|
| 64 |
+
|
| 65 |
+
```python
|
| 66 |
+
text_emb = model.encode_text(["a photo of a bus", "a photo of a dog"])
|
| 67 |
+
print(text_emb.shape) # (2, 1024) — one embedding per query
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
### Zero-shot classification
|
| 71 |
+
|
| 72 |
+
```python
|
| 73 |
+
import torch.nn.functional as F
|
| 74 |
+
|
| 75 |
+
classes = ["bus", "car", "dog", "cat"]
|
| 76 |
+
cls = F.normalize(out.cls_token[:, 0, :], dim=-1)
|
| 77 |
+
text_emb = F.normalize(model.encode_text(classes), dim=-1)
|
| 78 |
+
similarity = cls @ text_emb.T
|
| 79 |
+
print(classes[similarity.argmax()]) # predicted class
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
### Visualize spatial features
|
| 83 |
+
|
| 84 |
+
```python
|
| 85 |
+
import numpy as np
|
| 86 |
+
from sklearn.decomposition import PCA
|
| 87 |
+
|
| 88 |
+
feat = out.patch_tokens[0].detach().cpu().numpy()
|
| 89 |
+
rgb = PCA(n_components=3, whiten=True).fit_transform(feat).reshape(32, 32, 3)
|
| 90 |
+
rgb = 1 / (1 + np.exp(-2.0 * rgb)) # sigmoid for [0, 1] range with good contrast
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
## Model details
|
| 94 |
+
|
| 95 |
+
- ViT-L/14 vision encoder (24 layers, patch size 14, two CLS tokens) + 12-layer transformer text encoder
|
| 96 |
+
- Native resolution 448; other patch-multiple resolutions work via positional-embedding interpolation
|
| 97 |
+
- Preprocessing: images to `[0, 1]`, no normalization; SentencePiece tokenizer, lowercased, max 64 tokens
|
| 98 |
+
|
| 99 |
+
## License
|
| 100 |
+
|
| 101 |
+
Apache 2.0
|
| 102 |
+
|
| 103 |
+
## Citation
|
| 104 |
+
|
| 105 |
+
```bibtex
|
| 106 |
+
@inproceedings{maninis2025tips,
|
| 107 |
+
title = {{TIPS: Text-Image Pretraining with Spatial Awareness}},
|
| 108 |
+
author = {Maninis, Kevis-Kokitsi and Chen, Kaifeng and Ghosh, Soham and Karpur, Arjun and Chen, Koert and Xia, Ye and Cao, Bingyi and Salz, Daniel and Han, Guangxing and Dlabal, Jan and Gnanapragasam, Dan and Seyedhosseini, Mojtaba and Zhou, Howard and Araujo, Andre},
|
| 109 |
+
booktitle = {International Conference on Learning Representations (ICLR)},
|
| 110 |
+
year = {2025},
|
| 111 |
+
url = {https://arxiv.org/abs/2410.16512}
|
| 112 |
+
}
|
| 113 |
+
```
|
config.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"TIPSv2Model"
|
| 4 |
+
],
|
| 5 |
+
"auto_map": {
|
| 6 |
+
"AutoConfig": "configuration_tips.TIPSv2Config",
|
| 7 |
+
"AutoModel": "modeling_tips.TIPSv2Model"
|
| 8 |
+
},
|
| 9 |
+
"model_type": "tipsv2",
|
| 10 |
+
"temperature_init_value": 0.004205586854368448,
|
| 11 |
+
"text_config": {
|
| 12 |
+
"attention_dropout": 0.0,
|
| 13 |
+
"bos_token_id": null,
|
| 14 |
+
"eos_token_id": null,
|
| 15 |
+
"hidden_act": "relu",
|
| 16 |
+
"hidden_size": 1024,
|
| 17 |
+
"initializer_range": 0.02,
|
| 18 |
+
"intermediate_size": 4096,
|
| 19 |
+
"layer_norm_eps": 1e-05,
|
| 20 |
+
"max_position_embeddings": 64,
|
| 21 |
+
"model_type": "tipsv2_text_model",
|
| 22 |
+
"num_attention_heads": 16,
|
| 23 |
+
"num_hidden_layers": 12,
|
| 24 |
+
"pad_token_id": 0,
|
| 25 |
+
"pooling_epsilon": 1e-08,
|
| 26 |
+
"scale_sqrt_depth": true,
|
| 27 |
+
"vocab_size": 32000
|
| 28 |
+
},
|
| 29 |
+
"transformers_version": "5.14.1",
|
| 30 |
+
"vision_config": {
|
| 31 |
+
"apply_layernorm": true,
|
| 32 |
+
"attention_probs_dropout_prob": 0.0,
|
| 33 |
+
"drop_path_rate": 0.0,
|
| 34 |
+
"hidden_act": "gelu",
|
| 35 |
+
"hidden_dropout_prob": 0.0,
|
| 36 |
+
"hidden_size": 1024,
|
| 37 |
+
"image_size": 448,
|
| 38 |
+
"initializer_range": 0.02,
|
| 39 |
+
"layer_norm_eps": 1e-06,
|
| 40 |
+
"layerscale_value": 1.0,
|
| 41 |
+
"mlp_ratio": 4,
|
| 42 |
+
"model_type": "tipsv2_vision_model",
|
| 43 |
+
"num_attention_heads": 16,
|
| 44 |
+
"num_channels": 3,
|
| 45 |
+
"num_hidden_layers": 24,
|
| 46 |
+
"num_register_tokens": 1,
|
| 47 |
+
"out_features": [
|
| 48 |
+
"stage24"
|
| 49 |
+
],
|
| 50 |
+
"out_indices": [
|
| 51 |
+
24
|
| 52 |
+
],
|
| 53 |
+
"patch_size": 14,
|
| 54 |
+
"qkv_bias": true,
|
| 55 |
+
"reshape_hidden_states": true,
|
| 56 |
+
"stage_names": [
|
| 57 |
+
"stem",
|
| 58 |
+
"stage1",
|
| 59 |
+
"stage2",
|
| 60 |
+
"stage3",
|
| 61 |
+
"stage4",
|
| 62 |
+
"stage5",
|
| 63 |
+
"stage6",
|
| 64 |
+
"stage7",
|
| 65 |
+
"stage8",
|
| 66 |
+
"stage9",
|
| 67 |
+
"stage10",
|
| 68 |
+
"stage11",
|
| 69 |
+
"stage12",
|
| 70 |
+
"stage13",
|
| 71 |
+
"stage14",
|
| 72 |
+
"stage15",
|
| 73 |
+
"stage16",
|
| 74 |
+
"stage17",
|
| 75 |
+
"stage18",
|
| 76 |
+
"stage19",
|
| 77 |
+
"stage20",
|
| 78 |
+
"stage21",
|
| 79 |
+
"stage22",
|
| 80 |
+
"stage23",
|
| 81 |
+
"stage24"
|
| 82 |
+
],
|
| 83 |
+
"use_swiglu_ffn": false
|
| 84 |
+
}
|
| 85 |
+
}
|
configuration_tips.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""TIPSv2 model configuration."""
|
| 2 |
+
|
| 3 |
+
from transformers import PretrainedConfig
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
_VISION_FN_BY_GEOMETRY = {
|
| 7 |
+
(384, 12): "vit_small",
|
| 8 |
+
(768, 12): "vit_base",
|
| 9 |
+
(1024, 24): "vit_large",
|
| 10 |
+
(1152, 27): "vit_so400m",
|
| 11 |
+
(1536, 40): "vit_giant2",
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class TIPSv2Config(PretrainedConfig):
|
| 16 |
+
"""Configuration for TIPSv2 vision-language model."""
|
| 17 |
+
|
| 18 |
+
model_type = "tipsv2"
|
| 19 |
+
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
vision_config=None,
|
| 23 |
+
text_config=None,
|
| 24 |
+
temperature_init_value=0.01,
|
| 25 |
+
**kwargs,
|
| 26 |
+
):
|
| 27 |
+
super().__init__(**kwargs)
|
| 28 |
+
vision_config = vision_config or {}
|
| 29 |
+
text_config = text_config or {}
|
| 30 |
+
hidden_size = vision_config.get("hidden_size", 768)
|
| 31 |
+
num_hidden_layers = vision_config.get("num_hidden_layers", 12)
|
| 32 |
+
self.vision_fn = _VISION_FN_BY_GEOMETRY[(hidden_size, num_hidden_layers)]
|
| 33 |
+
self.embed_dim = hidden_size
|
| 34 |
+
self.patch_size = vision_config.get("patch_size", 14)
|
| 35 |
+
self.img_size = vision_config.get("image_size", 448)
|
| 36 |
+
self.ffn_layer = "swiglu" if vision_config.get("use_swiglu_ffn", False) else "mlp"
|
| 37 |
+
self.init_values = vision_config.get("layerscale_value", 1.0)
|
| 38 |
+
self.num_register_tokens = vision_config.get("num_register_tokens", 1)
|
| 39 |
+
self.text_hidden_size = text_config.get("hidden_size", 768)
|
| 40 |
+
self.text_mlp_dim = text_config.get("intermediate_size", 3072)
|
| 41 |
+
self.text_num_heads = text_config.get("num_attention_heads", 12)
|
| 42 |
+
self.text_num_layers = text_config.get("num_hidden_layers", 12)
|
| 43 |
+
self.vocab_size = text_config.get("vocab_size", 32000)
|
| 44 |
+
self.max_len = text_config.get("max_position_embeddings", 64)
|
| 45 |
+
self.temperature = temperature_init_value
|
image_encoder.py
ADDED
|
@@ -0,0 +1,1002 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
# ==============================================================================
|
| 15 |
+
|
| 16 |
+
"""Vision encoder implementation in PyTorch."""
|
| 17 |
+
|
| 18 |
+
import functools
|
| 19 |
+
import math
|
| 20 |
+
import os
|
| 21 |
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
|
| 22 |
+
import warnings
|
| 23 |
+
import torch
|
| 24 |
+
from torch import nn
|
| 25 |
+
import torch.nn.functional as F
|
| 26 |
+
import torch.utils.checkpoint
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class Mlp(nn.Module):
|
| 30 |
+
"""Transformer MLP, following DINOv2 implementation."""
|
| 31 |
+
|
| 32 |
+
def __init__(
|
| 33 |
+
self,
|
| 34 |
+
in_features: int,
|
| 35 |
+
hidden_features: Optional[int] = None,
|
| 36 |
+
out_features: Optional[int] = None,
|
| 37 |
+
act_layer: Callable[..., nn.Module] = nn.GELU,
|
| 38 |
+
drop: float = 0.0,
|
| 39 |
+
bias: bool = True,
|
| 40 |
+
) -> None:
|
| 41 |
+
super().__init__()
|
| 42 |
+
out_features = out_features or in_features
|
| 43 |
+
hidden_features = hidden_features or in_features
|
| 44 |
+
self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)
|
| 45 |
+
self.act = act_layer()
|
| 46 |
+
self.fc2 = nn.Linear(hidden_features, out_features, bias=bias)
|
| 47 |
+
self.drop = nn.Dropout(drop)
|
| 48 |
+
|
| 49 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 50 |
+
x = self.fc1(x)
|
| 51 |
+
x = self.act(x)
|
| 52 |
+
x = self.drop(x)
|
| 53 |
+
x = self.fc2(x)
|
| 54 |
+
x = self.drop(x)
|
| 55 |
+
return x
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def make_2tuple(x):
|
| 59 |
+
if isinstance(x, tuple):
|
| 60 |
+
assert len(x) == 2
|
| 61 |
+
return x
|
| 62 |
+
|
| 63 |
+
assert isinstance(x, int)
|
| 64 |
+
return (x, x)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class PatchEmbed(nn.Module):
|
| 68 |
+
"""2D image to patch embedding: (B,C,H,W) -> (B,N,D)."""
|
| 69 |
+
|
| 70 |
+
def __init__(
|
| 71 |
+
self,
|
| 72 |
+
img_size: Union[int, Tuple[int, int]] = 224,
|
| 73 |
+
patch_size: Union[int, Tuple[int, int]] = 16,
|
| 74 |
+
in_chans: int = 3,
|
| 75 |
+
embed_dim: int = 768,
|
| 76 |
+
norm_layer: Optional[Callable] = None, # pylint: disable=g-bare-generic
|
| 77 |
+
flatten_embedding: bool = True,
|
| 78 |
+
) -> None:
|
| 79 |
+
super().__init__()
|
| 80 |
+
|
| 81 |
+
image_hw = make_2tuple(img_size)
|
| 82 |
+
patch_hw = make_2tuple(patch_size)
|
| 83 |
+
patch_grid_size = (
|
| 84 |
+
image_hw[0] // patch_hw[0],
|
| 85 |
+
image_hw[1] // patch_hw[1],
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
self.img_size = image_hw
|
| 89 |
+
self.patch_size = patch_hw
|
| 90 |
+
self.patches_resolution = patch_grid_size
|
| 91 |
+
self.num_patches = patch_grid_size[0] * patch_grid_size[1]
|
| 92 |
+
|
| 93 |
+
self.in_chans = in_chans
|
| 94 |
+
self.embed_dim = embed_dim
|
| 95 |
+
|
| 96 |
+
self.flatten_embedding = flatten_embedding
|
| 97 |
+
|
| 98 |
+
self.proj = nn.Conv2d(
|
| 99 |
+
in_chans, embed_dim, kernel_size=patch_hw, stride=patch_hw
|
| 100 |
+
)
|
| 101 |
+
self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
|
| 102 |
+
|
| 103 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 104 |
+
_, _, h, w = x.shape
|
| 105 |
+
patch_h, patch_w = self.patch_size
|
| 106 |
+
|
| 107 |
+
assert (
|
| 108 |
+
h % patch_h == 0
|
| 109 |
+
), f"Input image height {h} is not a multiple of patch height {patch_h}"
|
| 110 |
+
assert (
|
| 111 |
+
w % patch_w == 0
|
| 112 |
+
), f"Input image width {w} is not a multiple of patch width: {patch_w}"
|
| 113 |
+
|
| 114 |
+
x = self.proj(x) # B C H W
|
| 115 |
+
h, w = x.size(2), x.size(3)
|
| 116 |
+
x = x.flatten(2).transpose(1, 2) # B HW C
|
| 117 |
+
x = self.norm(x)
|
| 118 |
+
if not self.flatten_embedding:
|
| 119 |
+
x = x.reshape(-1, h, w, self.embed_dim) # B H W C
|
| 120 |
+
return x
|
| 121 |
+
|
| 122 |
+
def flops(self) -> float:
|
| 123 |
+
ho, wo = self.patches_resolution
|
| 124 |
+
flops = (
|
| 125 |
+
ho
|
| 126 |
+
* wo
|
| 127 |
+
* self.embed_dim
|
| 128 |
+
* self.in_chans
|
| 129 |
+
* (self.patch_size[0] * self.patch_size[1])
|
| 130 |
+
)
|
| 131 |
+
if self.norm is not None:
|
| 132 |
+
flops += ho * wo * self.embed_dim
|
| 133 |
+
return flops
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
class SwiGLUFFN(nn.Module):
|
| 137 |
+
"""SwiGLU FFN layer, following DINOv2 implementation."""
|
| 138 |
+
|
| 139 |
+
def __init__(
|
| 140 |
+
self,
|
| 141 |
+
in_features: int,
|
| 142 |
+
hidden_features: Optional[int] = None,
|
| 143 |
+
out_features: Optional[int] = None,
|
| 144 |
+
act_layer: Callable[..., nn.Module] = None,
|
| 145 |
+
drop: float = 0.0,
|
| 146 |
+
bias: bool = True,
|
| 147 |
+
) -> None:
|
| 148 |
+
super().__init__()
|
| 149 |
+
out_features = out_features or in_features
|
| 150 |
+
hidden_features = hidden_features or in_features
|
| 151 |
+
self.w12 = nn.Linear(in_features, 2 * hidden_features, bias=bias)
|
| 152 |
+
self.w3 = nn.Linear(hidden_features, out_features, bias=bias)
|
| 153 |
+
|
| 154 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 155 |
+
x12 = self.w12(x)
|
| 156 |
+
x1, x2 = x12.chunk(2, dim=-1)
|
| 157 |
+
hidden = F.silu(x1) * x2
|
| 158 |
+
return self.w3(hidden)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
XFORMERS_ENABLED = os.environ.get("XFORMERS_DISABLED") is None
|
| 162 |
+
try:
|
| 163 |
+
if XFORMERS_ENABLED:
|
| 164 |
+
from xformers.ops import SwiGLU, memory_efficient_attention, unbind, fmha, scaled_index_add, index_select_cat # pylint: disable=g-multiple-import, g-import-not-at-top
|
| 165 |
+
|
| 166 |
+
XFORMERS_AVAILABLE = True
|
| 167 |
+
warnings.warn("xFormers is available (SwiGLU)")
|
| 168 |
+
else:
|
| 169 |
+
warnings.warn("xFormers is disabled (SwiGLU)")
|
| 170 |
+
raise ImportError
|
| 171 |
+
except ImportError:
|
| 172 |
+
SwiGLU = SwiGLUFFN
|
| 173 |
+
XFORMERS_AVAILABLE = False
|
| 174 |
+
|
| 175 |
+
warnings.warn("xFormers is not available (SwiGLU)")
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
class SwiGLUFFNFused(SwiGLU):
|
| 179 |
+
"""SwiGLU FFN layer, following DINOv2 implementation."""
|
| 180 |
+
|
| 181 |
+
def __init__(
|
| 182 |
+
self,
|
| 183 |
+
in_features: int,
|
| 184 |
+
hidden_features: Optional[int] = None,
|
| 185 |
+
out_features: Optional[int] = None,
|
| 186 |
+
act_layer: Callable[..., nn.Module] = None, # pylint: disable=unused-argument
|
| 187 |
+
drop: float = 0.0, # pylint: disable=unused-argument
|
| 188 |
+
bias: bool = True,
|
| 189 |
+
) -> None:
|
| 190 |
+
out_features = out_features or in_features
|
| 191 |
+
hidden_features = hidden_features or in_features
|
| 192 |
+
hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8
|
| 193 |
+
super().__init__(
|
| 194 |
+
in_features=in_features,
|
| 195 |
+
hidden_features=hidden_features,
|
| 196 |
+
out_features=out_features,
|
| 197 |
+
bias=bias,
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
class Attention(nn.Module):
|
| 202 |
+
"""Attention layer, following DINOv2 implementation."""
|
| 203 |
+
|
| 204 |
+
def __init__(
|
| 205 |
+
self,
|
| 206 |
+
dim: int,
|
| 207 |
+
num_heads: int = 8,
|
| 208 |
+
qkv_bias: bool = False,
|
| 209 |
+
proj_bias: bool = True,
|
| 210 |
+
attn_drop: float = 0.0,
|
| 211 |
+
proj_drop: float = 0.0,
|
| 212 |
+
) -> None:
|
| 213 |
+
super().__init__()
|
| 214 |
+
self.num_heads = num_heads
|
| 215 |
+
head_dim = dim // num_heads
|
| 216 |
+
self.scale = head_dim**-0.5
|
| 217 |
+
|
| 218 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 219 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 220 |
+
self.proj = nn.Linear(dim, dim, bias=proj_bias)
|
| 221 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 222 |
+
|
| 223 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 224 |
+
b_dim, n_dim, c_dim = x.shape
|
| 225 |
+
qkv = (
|
| 226 |
+
self.qkv(x)
|
| 227 |
+
.reshape(b_dim, n_dim, 3, self.num_heads, c_dim // self.num_heads)
|
| 228 |
+
.permute(2, 0, 3, 1, 4)
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
q, k, v = qkv[0] * self.scale, qkv[1], qkv[2]
|
| 232 |
+
attn = q @ k.transpose(-2, -1)
|
| 233 |
+
|
| 234 |
+
attn = attn.softmax(dim=-1)
|
| 235 |
+
attn = self.attn_drop(attn)
|
| 236 |
+
|
| 237 |
+
x = (attn @ v).transpose(1, 2).reshape(b_dim, n_dim, c_dim)
|
| 238 |
+
x = self.proj(x)
|
| 239 |
+
x = self.proj_drop(x)
|
| 240 |
+
return x
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
class MemEffAttention(Attention):
|
| 244 |
+
"""Memory Efficient Attention layer, following DINOv2 implementation."""
|
| 245 |
+
|
| 246 |
+
def forward(self, x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 247 |
+
if not XFORMERS_AVAILABLE:
|
| 248 |
+
if attn_bias is not None:
|
| 249 |
+
raise AssertionError("xFormers is required for using nested tensors")
|
| 250 |
+
return super().forward(x)
|
| 251 |
+
|
| 252 |
+
b_dim, n_dim, c_dim = x.shape
|
| 253 |
+
qkv = self.qkv(x).reshape(
|
| 254 |
+
b_dim, n_dim, 3, self.num_heads, c_dim // self.num_heads
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
q, k, v = unbind(qkv, 2)
|
| 258 |
+
|
| 259 |
+
x = memory_efficient_attention(q, k, v, attn_bias=attn_bias)
|
| 260 |
+
x = x.reshape([b_dim, n_dim, c_dim])
|
| 261 |
+
|
| 262 |
+
x = self.proj(x)
|
| 263 |
+
x = self.proj_drop(x)
|
| 264 |
+
return x
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
class LayerScale(nn.Module):
|
| 268 |
+
"""Layer scale, following DINOv2 implementation."""
|
| 269 |
+
|
| 270 |
+
def __init__(
|
| 271 |
+
self,
|
| 272 |
+
dim: int,
|
| 273 |
+
init_values: Union[float, torch.Tensor] = 1e-5,
|
| 274 |
+
inplace: bool = False,
|
| 275 |
+
) -> None:
|
| 276 |
+
super().__init__()
|
| 277 |
+
self.inplace = inplace
|
| 278 |
+
self.gamma = nn.Parameter(init_values * torch.ones(dim))
|
| 279 |
+
|
| 280 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 281 |
+
return x.mul_(self.gamma) if self.inplace else x * self.gamma
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def drop_path_impl(x, drop_prob: float = 0.0, training: bool = False):
|
| 285 |
+
if drop_prob == 0.0 or not training:
|
| 286 |
+
return x
|
| 287 |
+
keep_prob = 1 - drop_prob
|
| 288 |
+
shape = (x.shape[0],) + (1,) * (
|
| 289 |
+
x.ndim - 1
|
| 290 |
+
) # work with diff dim tensors, not just 2D ConvNets
|
| 291 |
+
random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
|
| 292 |
+
if keep_prob > 0.0:
|
| 293 |
+
random_tensor.div_(keep_prob)
|
| 294 |
+
output = x * random_tensor
|
| 295 |
+
return output
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
class DropPath(nn.Module):
|
| 299 |
+
"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."""
|
| 300 |
+
|
| 301 |
+
def __init__(self, drop_prob=None):
|
| 302 |
+
super(DropPath, self).__init__()
|
| 303 |
+
self.drop_prob = drop_prob
|
| 304 |
+
|
| 305 |
+
def forward(self, x):
|
| 306 |
+
return drop_path_impl(x, self.drop_prob, self.training)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
class Block(nn.Module):
|
| 310 |
+
"""Transformer Block Implementation, following DINOv2 implementation."""
|
| 311 |
+
|
| 312 |
+
def __init__(
|
| 313 |
+
self,
|
| 314 |
+
dim: int,
|
| 315 |
+
num_heads: int,
|
| 316 |
+
mlp_ratio: float = 4.0,
|
| 317 |
+
qkv_bias: bool = False,
|
| 318 |
+
proj_bias: bool = True,
|
| 319 |
+
ffn_bias: bool = True,
|
| 320 |
+
drop: float = 0.0,
|
| 321 |
+
attn_drop: float = 0.0,
|
| 322 |
+
init_values=None,
|
| 323 |
+
drop_path: float = 0.0,
|
| 324 |
+
act_layer: Callable[..., nn.Module] = nn.GELU,
|
| 325 |
+
norm_layer: Callable[..., nn.Module] = nn.LayerNorm,
|
| 326 |
+
attn_class: Callable[..., nn.Module] = Attention,
|
| 327 |
+
ffn_layer: Callable[..., nn.Module] = Mlp,
|
| 328 |
+
) -> None:
|
| 329 |
+
super().__init__()
|
| 330 |
+
self.norm1 = norm_layer(dim)
|
| 331 |
+
self.attn = attn_class(
|
| 332 |
+
dim,
|
| 333 |
+
num_heads=num_heads,
|
| 334 |
+
qkv_bias=qkv_bias,
|
| 335 |
+
proj_bias=proj_bias,
|
| 336 |
+
attn_drop=attn_drop,
|
| 337 |
+
proj_drop=drop,
|
| 338 |
+
)
|
| 339 |
+
self.ls1 = (
|
| 340 |
+
LayerScale(dim, init_values=init_values)
|
| 341 |
+
if init_values
|
| 342 |
+
else nn.Identity()
|
| 343 |
+
)
|
| 344 |
+
self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
|
| 345 |
+
|
| 346 |
+
self.norm2 = norm_layer(dim)
|
| 347 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 348 |
+
self.mlp = ffn_layer(
|
| 349 |
+
in_features=dim,
|
| 350 |
+
hidden_features=mlp_hidden_dim,
|
| 351 |
+
act_layer=act_layer,
|
| 352 |
+
drop=drop,
|
| 353 |
+
bias=ffn_bias,
|
| 354 |
+
)
|
| 355 |
+
self.ls2 = (
|
| 356 |
+
LayerScale(dim, init_values=init_values)
|
| 357 |
+
if init_values
|
| 358 |
+
else nn.Identity()
|
| 359 |
+
)
|
| 360 |
+
self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
|
| 361 |
+
|
| 362 |
+
self.sample_drop_ratio = drop_path
|
| 363 |
+
|
| 364 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 365 |
+
def attn_residual_func(x: torch.Tensor) -> torch.Tensor:
|
| 366 |
+
return self.ls1(self.attn(self.norm1(x)))
|
| 367 |
+
|
| 368 |
+
def ffn_residual_func(x: torch.Tensor) -> torch.Tensor:
|
| 369 |
+
return self.ls2(self.mlp(self.norm2(x)))
|
| 370 |
+
|
| 371 |
+
if self.training and self.sample_drop_ratio > 0.1:
|
| 372 |
+
# the overhead is compensated only for a drop path rate larger than 0.1
|
| 373 |
+
x = drop_add_residual_stochastic_depth(
|
| 374 |
+
x,
|
| 375 |
+
residual_func=attn_residual_func,
|
| 376 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 377 |
+
)
|
| 378 |
+
x = drop_add_residual_stochastic_depth(
|
| 379 |
+
x,
|
| 380 |
+
residual_func=ffn_residual_func,
|
| 381 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 382 |
+
)
|
| 383 |
+
elif self.training and self.sample_drop_ratio > 0.0:
|
| 384 |
+
x = x + self.drop_path1(attn_residual_func(x))
|
| 385 |
+
x = x + self.drop_path1(ffn_residual_func(x))
|
| 386 |
+
else:
|
| 387 |
+
x = x + attn_residual_func(x)
|
| 388 |
+
x = x + ffn_residual_func(x)
|
| 389 |
+
return x
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
def drop_add_residual_stochastic_depth(
|
| 393 |
+
x: torch.Tensor,
|
| 394 |
+
residual_func: Callable[[torch.Tensor], torch.Tensor],
|
| 395 |
+
sample_drop_ratio: float = 0.0,
|
| 396 |
+
) -> torch.Tensor:
|
| 397 |
+
"""This function is taken from the original implementation in DINOv2 to implement stochastic depth in the image encoder."""
|
| 398 |
+
# 1) extract subset using permutation
|
| 399 |
+
b, _, _ = x.shape
|
| 400 |
+
sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
|
| 401 |
+
brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
|
| 402 |
+
x_subset = x[brange]
|
| 403 |
+
|
| 404 |
+
# 2) apply residual_func to get residual
|
| 405 |
+
residual = residual_func(x_subset)
|
| 406 |
+
|
| 407 |
+
x_flat = x.flatten(1)
|
| 408 |
+
residual = residual.flatten(1)
|
| 409 |
+
|
| 410 |
+
residual_scale_factor = b / sample_subset_size
|
| 411 |
+
|
| 412 |
+
# 3) add the residual
|
| 413 |
+
x_plus_residual = torch.index_add(
|
| 414 |
+
x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor
|
| 415 |
+
)
|
| 416 |
+
return x_plus_residual.view_as(x)
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def get_branges_scales(x, sample_drop_ratio=0.0):
|
| 420 |
+
b, _, _ = x.shape
|
| 421 |
+
sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)
|
| 422 |
+
brange = (torch.randperm(b, device=x.device))[:sample_subset_size]
|
| 423 |
+
residual_scale_factor = b / sample_subset_size
|
| 424 |
+
return brange, residual_scale_factor
|
| 425 |
+
|
| 426 |
+
|
| 427 |
+
def add_residual(
|
| 428 |
+
x, brange, residual, residual_scale_factor, scaling_vector=None
|
| 429 |
+
):
|
| 430 |
+
"""Implement residual addition in the image encoder."""
|
| 431 |
+
if scaling_vector is None:
|
| 432 |
+
x_flat = x.flatten(1)
|
| 433 |
+
residual = residual.flatten(1)
|
| 434 |
+
x_plus_residual = torch.index_add(
|
| 435 |
+
x_flat,
|
| 436 |
+
0,
|
| 437 |
+
brange,
|
| 438 |
+
residual.to(dtype=x.dtype),
|
| 439 |
+
alpha=residual_scale_factor,
|
| 440 |
+
)
|
| 441 |
+
else:
|
| 442 |
+
x_plus_residual = scaled_index_add(
|
| 443 |
+
x,
|
| 444 |
+
brange,
|
| 445 |
+
residual.to(dtype=x.dtype),
|
| 446 |
+
scaling=scaling_vector,
|
| 447 |
+
alpha=residual_scale_factor,
|
| 448 |
+
)
|
| 449 |
+
return x_plus_residual
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
attn_bias_cache: Dict[Tuple, Any] = {} # pylint: disable=g-bare-generic
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
def get_attn_bias_and_cat(x_list, branges=None):
|
| 456 |
+
"""this will perform the index select, cat the tensors, and provide the attn_bias from cache."""
|
| 457 |
+
batch_sizes = (
|
| 458 |
+
[b.shape[0] for b in branges]
|
| 459 |
+
if branges is not None
|
| 460 |
+
else [x.shape[0] for x in x_list]
|
| 461 |
+
)
|
| 462 |
+
all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list))
|
| 463 |
+
if all_shapes not in attn_bias_cache.keys():
|
| 464 |
+
seqlens = []
|
| 465 |
+
for b, x in zip(batch_sizes, x_list):
|
| 466 |
+
for _ in range(b):
|
| 467 |
+
seqlens.append(x.shape[1])
|
| 468 |
+
attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens)
|
| 469 |
+
attn_bias._batch_sizes = batch_sizes # pylint: disable=protected-access
|
| 470 |
+
attn_bias_cache[all_shapes] = attn_bias
|
| 471 |
+
|
| 472 |
+
if branges is not None:
|
| 473 |
+
cat_tensors = index_select_cat(
|
| 474 |
+
[x.flatten(1) for x in x_list], branges
|
| 475 |
+
).view(1, -1, x_list[0].shape[-1])
|
| 476 |
+
else:
|
| 477 |
+
tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list)
|
| 478 |
+
cat_tensors = torch.cat(tensors_bs1, dim=1)
|
| 479 |
+
|
| 480 |
+
return attn_bias_cache[all_shapes], cat_tensors
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def drop_add_residual_stochastic_depth_list(
|
| 484 |
+
x_list: List[torch.Tensor],
|
| 485 |
+
residual_func: Callable[[torch.Tensor, Any], torch.Tensor],
|
| 486 |
+
sample_drop_ratio: float = 0.0,
|
| 487 |
+
scaling_vector=None,
|
| 488 |
+
) -> torch.Tensor:
|
| 489 |
+
"""Add residual to a list of tensors."""
|
| 490 |
+
# 1) generate random set of indices for dropping samples in the batch.
|
| 491 |
+
branges_scales = [
|
| 492 |
+
get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list
|
| 493 |
+
]
|
| 494 |
+
branges = [s[0] for s in branges_scales]
|
| 495 |
+
residual_scale_factors = [s[1] for s in branges_scales]
|
| 496 |
+
|
| 497 |
+
# 2) get attention bias and index+concat the tensors.
|
| 498 |
+
attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges)
|
| 499 |
+
|
| 500 |
+
# 3) apply residual_func to get residual, and split the result.
|
| 501 |
+
residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias)) # type: ignore
|
| 502 |
+
|
| 503 |
+
outputs = []
|
| 504 |
+
for x, brange, residual, residual_scale_factor in zip(
|
| 505 |
+
x_list, branges, residual_list, residual_scale_factors
|
| 506 |
+
):
|
| 507 |
+
outputs.append(
|
| 508 |
+
add_residual(
|
| 509 |
+
x, brange, residual, residual_scale_factor, scaling_vector
|
| 510 |
+
).view_as(x)
|
| 511 |
+
)
|
| 512 |
+
return outputs
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
class NestedTensorBlock(Block):
|
| 516 |
+
"""Nested tensor block implementation."""
|
| 517 |
+
|
| 518 |
+
def forward_nested(self, x_list: List[torch.Tensor]) -> List[torch.Tensor]:
|
| 519 |
+
"""x_list contains a list of tensors to nest together and run."""
|
| 520 |
+
assert isinstance(self.attn, MemEffAttention)
|
| 521 |
+
|
| 522 |
+
if self.training and self.sample_drop_ratio > 0.0:
|
| 523 |
+
|
| 524 |
+
def attn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 525 |
+
return self.attn(self.norm1(x), attn_bias=attn_bias)
|
| 526 |
+
|
| 527 |
+
def ffn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 528 |
+
del attn_bias
|
| 529 |
+
return self.mlp(self.norm2(x))
|
| 530 |
+
|
| 531 |
+
x_list = drop_add_residual_stochastic_depth_list(
|
| 532 |
+
x_list,
|
| 533 |
+
residual_func=attn_residual_func,
|
| 534 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 535 |
+
scaling_vector=self.ls1.gamma
|
| 536 |
+
if isinstance(self.ls1, LayerScale)
|
| 537 |
+
else None,
|
| 538 |
+
)
|
| 539 |
+
x_list = drop_add_residual_stochastic_depth_list(
|
| 540 |
+
x_list,
|
| 541 |
+
residual_func=ffn_residual_func,
|
| 542 |
+
sample_drop_ratio=self.sample_drop_ratio,
|
| 543 |
+
scaling_vector=self.ls2.gamma
|
| 544 |
+
if isinstance(self.ls1, LayerScale)
|
| 545 |
+
else None,
|
| 546 |
+
)
|
| 547 |
+
return x_list
|
| 548 |
+
else:
|
| 549 |
+
|
| 550 |
+
def attn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 551 |
+
return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias))
|
| 552 |
+
|
| 553 |
+
def ffn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor:
|
| 554 |
+
del attn_bias
|
| 555 |
+
return self.ls2(self.mlp(self.norm2(x)))
|
| 556 |
+
|
| 557 |
+
attn_bias, x = get_attn_bias_and_cat(x_list)
|
| 558 |
+
x = x + attn_residual_func(x, attn_bias=attn_bias)
|
| 559 |
+
x = x + ffn_residual_func(x)
|
| 560 |
+
return attn_bias.split(x)
|
| 561 |
+
|
| 562 |
+
def forward(self, x):
|
| 563 |
+
if isinstance(x, torch.Tensor):
|
| 564 |
+
return super().forward(x)
|
| 565 |
+
elif isinstance(x, list):
|
| 566 |
+
if not XFORMERS_AVAILABLE:
|
| 567 |
+
raise AssertionError("xFormers is required for using nested tensors")
|
| 568 |
+
return self.forward_nested(x)
|
| 569 |
+
else:
|
| 570 |
+
raise AssertionError
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
def named_apply(
|
| 574 |
+
fn: Callable, # pylint: disable=g-bare-generic
|
| 575 |
+
module: nn.Module,
|
| 576 |
+
name="",
|
| 577 |
+
depth_first=True,
|
| 578 |
+
include_root=False,
|
| 579 |
+
) -> nn.Module:
|
| 580 |
+
"""Apply a function to a module and its children."""
|
| 581 |
+
if not depth_first and include_root:
|
| 582 |
+
fn(module=module, name=name)
|
| 583 |
+
for child_name, child_module in module.named_children():
|
| 584 |
+
child_name = ".".join((name, child_name)) if name else child_name
|
| 585 |
+
named_apply(
|
| 586 |
+
fn=fn,
|
| 587 |
+
module=child_module,
|
| 588 |
+
name=child_name,
|
| 589 |
+
depth_first=depth_first,
|
| 590 |
+
include_root=True,
|
| 591 |
+
)
|
| 592 |
+
if depth_first and include_root:
|
| 593 |
+
fn(module=module, name=name)
|
| 594 |
+
return module
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
class BlockChunk(nn.ModuleList):
|
| 598 |
+
|
| 599 |
+
def forward(self, x):
|
| 600 |
+
for b in self:
|
| 601 |
+
x = b(x)
|
| 602 |
+
return x
|
| 603 |
+
|
| 604 |
+
|
| 605 |
+
class VisionTransformer(nn.Module):
|
| 606 |
+
"""Vision Transformer implementation."""
|
| 607 |
+
|
| 608 |
+
def __init__(
|
| 609 |
+
self,
|
| 610 |
+
img_size=224,
|
| 611 |
+
patch_size=16,
|
| 612 |
+
in_chans=3,
|
| 613 |
+
embed_dim=768,
|
| 614 |
+
depth=12,
|
| 615 |
+
num_heads=12,
|
| 616 |
+
mlp_ratio=4.0,
|
| 617 |
+
qkv_bias=True,
|
| 618 |
+
ffn_bias=True,
|
| 619 |
+
proj_bias=True,
|
| 620 |
+
drop_path_rate=0.0,
|
| 621 |
+
drop_path_uniform=False,
|
| 622 |
+
init_values=None, # for layerscale: None or 0 => no layerscale
|
| 623 |
+
embed_layer=PatchEmbed,
|
| 624 |
+
act_layer=nn.GELU,
|
| 625 |
+
block_fn=Block,
|
| 626 |
+
ffn_layer="mlp",
|
| 627 |
+
block_chunks=1,
|
| 628 |
+
num_register_tokens=0,
|
| 629 |
+
interpolate_antialias=False,
|
| 630 |
+
interpolate_offset=0.1,
|
| 631 |
+
):
|
| 632 |
+
"""Defines the Vision Transformer model.
|
| 633 |
+
|
| 634 |
+
Args:
|
| 635 |
+
img_size (int, tuple): input image size
|
| 636 |
+
patch_size (int, tuple): patch size
|
| 637 |
+
in_chans (int): number of input channels
|
| 638 |
+
embed_dim (int): embedding dimension
|
| 639 |
+
depth (int): depth of transformer
|
| 640 |
+
num_heads (int): number of attention heads
|
| 641 |
+
mlp_ratio (int): ratio of mlp hidden dim to embedding dim
|
| 642 |
+
qkv_bias (bool): enable bias for qkv if True
|
| 643 |
+
ffn_bias (bool): enable bias for ffn if True
|
| 644 |
+
proj_bias (bool): enable bias for proj in attn if True
|
| 645 |
+
drop_path_rate (float): stochastic depth rate
|
| 646 |
+
drop_path_uniform (bool): apply uniform drop rate across blocks
|
| 647 |
+
init_values (float): layer-scale init values
|
| 648 |
+
embed_layer (nn.Module): patch embedding layer
|
| 649 |
+
act_layer (nn.Module): MLP activation layer
|
| 650 |
+
block_fn (nn.Module): transformer block class
|
| 651 |
+
ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
|
| 652 |
+
block_chunks: (int) split block sequence into block_chunks units for FSDP
|
| 653 |
+
wrap
|
| 654 |
+
num_register_tokens: (int) number of extra cls tokens (so-called
|
| 655 |
+
"registers")
|
| 656 |
+
interpolate_antialias: (str) flag to apply anti-aliasing when
|
| 657 |
+
interpolating positional embeddings
|
| 658 |
+
interpolate_offset: (float) work-around offset to apply when interpolating
|
| 659 |
+
positional embeddings
|
| 660 |
+
"""
|
| 661 |
+
super().__init__()
|
| 662 |
+
norm_layer = functools.partial(nn.LayerNorm, eps=1e-6)
|
| 663 |
+
|
| 664 |
+
self.num_features = self.embed_dim = (
|
| 665 |
+
embed_dim # num_features for consistency with other models
|
| 666 |
+
)
|
| 667 |
+
self.num_tokens = 1
|
| 668 |
+
self.n_blocks = depth
|
| 669 |
+
self.num_heads = num_heads
|
| 670 |
+
self.patch_size = patch_size
|
| 671 |
+
self.num_register_tokens = num_register_tokens
|
| 672 |
+
self.interpolate_antialias = interpolate_antialias
|
| 673 |
+
self.interpolate_offset = interpolate_offset
|
| 674 |
+
|
| 675 |
+
self.patch_embed = embed_layer(
|
| 676 |
+
img_size=img_size,
|
| 677 |
+
patch_size=patch_size,
|
| 678 |
+
in_chans=in_chans,
|
| 679 |
+
embed_dim=embed_dim,
|
| 680 |
+
)
|
| 681 |
+
num_patches = self.patch_embed.num_patches
|
| 682 |
+
|
| 683 |
+
self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
|
| 684 |
+
self.pos_embed = nn.Parameter(
|
| 685 |
+
torch.zeros(1, num_patches + self.num_tokens, embed_dim)
|
| 686 |
+
)
|
| 687 |
+
assert num_register_tokens >= 0
|
| 688 |
+
self.register_tokens = (
|
| 689 |
+
nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim))
|
| 690 |
+
if num_register_tokens
|
| 691 |
+
else None
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
if drop_path_uniform:
|
| 695 |
+
dpr = [drop_path_rate] * depth
|
| 696 |
+
else:
|
| 697 |
+
dpr = [
|
| 698 |
+
drop_path_rate * i / max(depth - 1, 1) for i in range(depth)
|
| 699 |
+
] # stochastic depth decay rule
|
| 700 |
+
|
| 701 |
+
if ffn_layer == "mlp":
|
| 702 |
+
ffn_layer = Mlp
|
| 703 |
+
elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
|
| 704 |
+
ffn_layer = SwiGLUFFNFused
|
| 705 |
+
else:
|
| 706 |
+
raise NotImplementedError
|
| 707 |
+
|
| 708 |
+
blocks_list = [
|
| 709 |
+
block_fn(
|
| 710 |
+
dim=embed_dim,
|
| 711 |
+
num_heads=num_heads,
|
| 712 |
+
mlp_ratio=mlp_ratio,
|
| 713 |
+
qkv_bias=qkv_bias,
|
| 714 |
+
proj_bias=proj_bias,
|
| 715 |
+
ffn_bias=ffn_bias,
|
| 716 |
+
drop_path=dpr[i],
|
| 717 |
+
norm_layer=norm_layer,
|
| 718 |
+
act_layer=act_layer,
|
| 719 |
+
ffn_layer=ffn_layer,
|
| 720 |
+
init_values=init_values,
|
| 721 |
+
)
|
| 722 |
+
for i in range(depth)
|
| 723 |
+
]
|
| 724 |
+
if block_chunks > 0:
|
| 725 |
+
self.chunked_blocks = True
|
| 726 |
+
chunked_blocks = []
|
| 727 |
+
chunksize = depth // block_chunks
|
| 728 |
+
for i in range(0, depth, chunksize):
|
| 729 |
+
# this is to keep the block index consistent if we chunk the block list
|
| 730 |
+
chunked_blocks.append(
|
| 731 |
+
[nn.Identity()] * i + blocks_list[i : i + chunksize]
|
| 732 |
+
)
|
| 733 |
+
self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
|
| 734 |
+
else:
|
| 735 |
+
self.chunked_blocks = False
|
| 736 |
+
self.blocks = nn.ModuleList(blocks_list)
|
| 737 |
+
|
| 738 |
+
self.norm = norm_layer(embed_dim)
|
| 739 |
+
self.head = nn.Identity()
|
| 740 |
+
|
| 741 |
+
self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))
|
| 742 |
+
|
| 743 |
+
self.init_weights()
|
| 744 |
+
|
| 745 |
+
def init_weights(self):
|
| 746 |
+
nn.init.trunc_normal_(self.pos_embed, std=0.02)
|
| 747 |
+
nn.init.normal_(self.cls_token, std=1e-6)
|
| 748 |
+
if self.register_tokens is not None:
|
| 749 |
+
nn.init.normal_(self.register_tokens, std=1e-6)
|
| 750 |
+
named_apply(init_weights_vit_timm, self)
|
| 751 |
+
|
| 752 |
+
def interpolate_pos_encoding(self, x, w, h):
|
| 753 |
+
previous_dtype = x.dtype
|
| 754 |
+
npatch = x.shape[1] - 1
|
| 755 |
+
num_patches = self.pos_embed.shape[1] - 1
|
| 756 |
+
if npatch == num_patches and w == h:
|
| 757 |
+
return self.pos_embed
|
| 758 |
+
pos_embed = self.pos_embed.float()
|
| 759 |
+
class_pos_embed = pos_embed[:, 0]
|
| 760 |
+
patch_pos_embed = pos_embed[:, 1:]
|
| 761 |
+
dim = x.shape[-1]
|
| 762 |
+
w0 = w // self.patch_size
|
| 763 |
+
h0 = h // self.patch_size
|
| 764 |
+
num_patches_dim = int(
|
| 765 |
+
math.sqrt(num_patches)
|
| 766 |
+
) # Recover the number of patches in each dimension
|
| 767 |
+
assert num_patches == num_patches_dim * num_patches_dim
|
| 768 |
+
kwargs = {}
|
| 769 |
+
if self.interpolate_offset:
|
| 770 |
+
sx = float(w0 + self.interpolate_offset) / num_patches_dim
|
| 771 |
+
sy = float(h0 + self.interpolate_offset) / num_patches_dim
|
| 772 |
+
kwargs["scale_factor"] = (sx, sy)
|
| 773 |
+
else:
|
| 774 |
+
# Simply specify an output size instead of a scale factor
|
| 775 |
+
kwargs["size"] = (w0, h0)
|
| 776 |
+
patch_pos_embed = nn.functional.interpolate(
|
| 777 |
+
patch_pos_embed.reshape(
|
| 778 |
+
1, num_patches_dim, num_patches_dim, dim
|
| 779 |
+
).permute(0, 3, 1, 2),
|
| 780 |
+
mode="bilinear",
|
| 781 |
+
antialias=self.interpolate_antialias,
|
| 782 |
+
**kwargs,
|
| 783 |
+
)
|
| 784 |
+
assert (w0, h0) == patch_pos_embed.shape[-2:]
|
| 785 |
+
patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
|
| 786 |
+
return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(
|
| 787 |
+
previous_dtype
|
| 788 |
+
)
|
| 789 |
+
|
| 790 |
+
def prepare_tokens_with_masks(self, x, masks=None):
|
| 791 |
+
_, _, w, h = x.shape
|
| 792 |
+
x = self.patch_embed(x)
|
| 793 |
+
if masks is not None:
|
| 794 |
+
x = torch.where(
|
| 795 |
+
masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x
|
| 796 |
+
)
|
| 797 |
+
|
| 798 |
+
x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
|
| 799 |
+
x = x + self.interpolate_pos_encoding(x, w, h)
|
| 800 |
+
|
| 801 |
+
if self.register_tokens is not None:
|
| 802 |
+
x = torch.cat(
|
| 803 |
+
(
|
| 804 |
+
x[:, :1],
|
| 805 |
+
self.register_tokens.expand(x.shape[0], -1, -1),
|
| 806 |
+
x[:, 1:],
|
| 807 |
+
),
|
| 808 |
+
dim=1,
|
| 809 |
+
)
|
| 810 |
+
|
| 811 |
+
return x
|
| 812 |
+
|
| 813 |
+
def forward_features_list(self, x_list, masks_list):
|
| 814 |
+
x = [
|
| 815 |
+
self.prepare_tokens_with_masks(x, masks)
|
| 816 |
+
for x, masks in zip(x_list, masks_list)
|
| 817 |
+
]
|
| 818 |
+
for blk in self.blocks:
|
| 819 |
+
x = blk(x)
|
| 820 |
+
|
| 821 |
+
all_x = x
|
| 822 |
+
output = []
|
| 823 |
+
for x, masks in zip(all_x, masks_list):
|
| 824 |
+
x_norm = self.norm(x)
|
| 825 |
+
output.append({
|
| 826 |
+
"x_norm_1st_clstoken": x_norm[:, :1],
|
| 827 |
+
"x_norm_2nd_clstoken": x_norm[:, 1 : self.num_register_tokens + 1],
|
| 828 |
+
"x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
|
| 829 |
+
"x_prenorm": x,
|
| 830 |
+
"masks": masks,
|
| 831 |
+
})
|
| 832 |
+
return output
|
| 833 |
+
|
| 834 |
+
def forward_features(self, x, masks=None):
|
| 835 |
+
if isinstance(x, list):
|
| 836 |
+
return self.forward_features_list(x, masks)
|
| 837 |
+
|
| 838 |
+
x = self.prepare_tokens_with_masks(x, masks)
|
| 839 |
+
|
| 840 |
+
for blk in self.blocks:
|
| 841 |
+
x = blk(x)
|
| 842 |
+
|
| 843 |
+
x_norm = self.norm(x)
|
| 844 |
+
return {
|
| 845 |
+
"x_norm_1st_clstoken": x_norm[:, :1],
|
| 846 |
+
"x_norm_2nd_clstoken": x_norm[:, 1 : self.num_register_tokens + 1],
|
| 847 |
+
"x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
|
| 848 |
+
"x_prenorm": x,
|
| 849 |
+
"masks": masks,
|
| 850 |
+
}
|
| 851 |
+
|
| 852 |
+
def _get_intermediate_layers_not_chunked(self, x, n=1):
|
| 853 |
+
x = self.prepare_tokens_with_masks(x)
|
| 854 |
+
# If n is an int, take the n last blocks. If it's a list, take them
|
| 855 |
+
output, total_block_len = [], len(self.blocks)
|
| 856 |
+
blocks_to_take = (
|
| 857 |
+
range(total_block_len - n, total_block_len) if isinstance(n, int) else n
|
| 858 |
+
)
|
| 859 |
+
for i, blk in enumerate(self.blocks):
|
| 860 |
+
x = blk(x)
|
| 861 |
+
if i in blocks_to_take:
|
| 862 |
+
output.append(x)
|
| 863 |
+
assert len(output) == len(
|
| 864 |
+
blocks_to_take
|
| 865 |
+
), f"only {len(output)} / {len(blocks_to_take)} blocks found"
|
| 866 |
+
return output
|
| 867 |
+
|
| 868 |
+
def _get_intermediate_layers_chunked(self, x, n=1):
|
| 869 |
+
x = self.prepare_tokens_with_masks(x)
|
| 870 |
+
output, i, total_block_len = [], 0, len(self.blocks[-1])
|
| 871 |
+
# If n is an int, take the n last blocks. If it's a list, take them
|
| 872 |
+
blocks_to_take = (
|
| 873 |
+
range(total_block_len - n, total_block_len) if isinstance(n, int) else n
|
| 874 |
+
)
|
| 875 |
+
for block_chunk in self.blocks:
|
| 876 |
+
for blk in block_chunk[i:]: # Passing the nn.Identity()
|
| 877 |
+
x = blk(x)
|
| 878 |
+
if i in blocks_to_take:
|
| 879 |
+
output.append(x)
|
| 880 |
+
i += 1
|
| 881 |
+
assert len(output) == len(
|
| 882 |
+
blocks_to_take
|
| 883 |
+
), f"only {len(output)} / {len(blocks_to_take)} blocks found"
|
| 884 |
+
return output
|
| 885 |
+
|
| 886 |
+
def get_intermediate_layers(
|
| 887 |
+
self,
|
| 888 |
+
x: torch.torch.Tensor,
|
| 889 |
+
n: Union[int, Sequence] = 1, # Layers or n last layers to take # pylint: disable=g-bare-generic
|
| 890 |
+
reshape: bool = False,
|
| 891 |
+
return_class_token: bool = False,
|
| 892 |
+
norm=True,
|
| 893 |
+
) -> Tuple[Union[torch.torch.Tensor, Tuple[torch.torch.Tensor]]]: # pylint: disable=g-one-element-tuple
|
| 894 |
+
if self.chunked_blocks:
|
| 895 |
+
outputs = self._get_intermediate_layers_chunked(x, n)
|
| 896 |
+
else:
|
| 897 |
+
outputs = self._get_intermediate_layers_not_chunked(x, n)
|
| 898 |
+
if norm:
|
| 899 |
+
outputs = [self.norm(out) for out in outputs]
|
| 900 |
+
class_tokens = [out[:, 0] for out in outputs]
|
| 901 |
+
outputs = [out[:, 1 + self.num_register_tokens :] for out in outputs]
|
| 902 |
+
if reshape:
|
| 903 |
+
batch_size, _, w, h = x.shape
|
| 904 |
+
outputs = [
|
| 905 |
+
out.reshape(
|
| 906 |
+
batch_size, w // self.patch_size, h // self.patch_size, -1
|
| 907 |
+
)
|
| 908 |
+
.permute(0, 3, 1, 2)
|
| 909 |
+
.contiguous()
|
| 910 |
+
for out in outputs
|
| 911 |
+
]
|
| 912 |
+
if return_class_token:
|
| 913 |
+
return tuple(zip(outputs, class_tokens))
|
| 914 |
+
return tuple(outputs)
|
| 915 |
+
|
| 916 |
+
def forward(self, *args, is_training=False, **kwargs):
|
| 917 |
+
ret = self.forward_features(*args, **kwargs)
|
| 918 |
+
if is_training:
|
| 919 |
+
return ret
|
| 920 |
+
else:
|
| 921 |
+
return self.head(ret["x_norm_1st_clstoken"]), self.head(
|
| 922 |
+
ret["x_norm_2nd_clstoken"]
|
| 923 |
+
), ret["x_norm_patchtokens"]
|
| 924 |
+
|
| 925 |
+
|
| 926 |
+
def init_weights_vit_timm(module: nn.Module, name: str = ""): # pylint: disable=unused-argument
|
| 927 |
+
"""ViT weight initialization, original timm impl (for reproducibility)."""
|
| 928 |
+
if isinstance(module, nn.Linear):
|
| 929 |
+
nn.init.trunc_normal_(module.weight, std=0.02)
|
| 930 |
+
if module.bias is not None:
|
| 931 |
+
nn.init.zeros_(module.bias)
|
| 932 |
+
|
| 933 |
+
|
| 934 |
+
def vit_small(patch_size=14, **kwargs):
|
| 935 |
+
model = VisionTransformer(
|
| 936 |
+
patch_size=patch_size,
|
| 937 |
+
embed_dim=384,
|
| 938 |
+
depth=12,
|
| 939 |
+
num_heads=6,
|
| 940 |
+
mlp_ratio=4,
|
| 941 |
+
block_fn=functools.partial(Block, attn_class=MemEffAttention),
|
| 942 |
+
num_register_tokens=1,
|
| 943 |
+
**kwargs,
|
| 944 |
+
)
|
| 945 |
+
return model
|
| 946 |
+
|
| 947 |
+
|
| 948 |
+
def vit_base(patch_size=14, **kwargs):
|
| 949 |
+
model = VisionTransformer(
|
| 950 |
+
patch_size=patch_size,
|
| 951 |
+
embed_dim=768,
|
| 952 |
+
depth=12,
|
| 953 |
+
num_heads=12,
|
| 954 |
+
mlp_ratio=4,
|
| 955 |
+
block_fn=functools.partial(Block, attn_class=MemEffAttention),
|
| 956 |
+
num_register_tokens=1,
|
| 957 |
+
**kwargs,
|
| 958 |
+
)
|
| 959 |
+
return model
|
| 960 |
+
|
| 961 |
+
|
| 962 |
+
def vit_large(patch_size=14, **kwargs):
|
| 963 |
+
model = VisionTransformer(
|
| 964 |
+
patch_size=patch_size,
|
| 965 |
+
embed_dim=1024,
|
| 966 |
+
depth=24,
|
| 967 |
+
num_heads=16,
|
| 968 |
+
mlp_ratio=4,
|
| 969 |
+
block_fn=functools.partial(Block, attn_class=MemEffAttention),
|
| 970 |
+
num_register_tokens=1,
|
| 971 |
+
**kwargs,
|
| 972 |
+
)
|
| 973 |
+
return model
|
| 974 |
+
|
| 975 |
+
|
| 976 |
+
def vit_so400m(patch_size=14, **kwargs):
|
| 977 |
+
"""SoViT 400M model (https://arxiv.org/abs/2305.13035)."""
|
| 978 |
+
model = VisionTransformer(
|
| 979 |
+
patch_size=patch_size,
|
| 980 |
+
embed_dim=1152,
|
| 981 |
+
depth=27,
|
| 982 |
+
num_heads=16,
|
| 983 |
+
mlp_ratio=4304 / 1152,
|
| 984 |
+
block_fn=functools.partial(Block, attn_class=MemEffAttention),
|
| 985 |
+
num_register_tokens=1,
|
| 986 |
+
**kwargs,
|
| 987 |
+
)
|
| 988 |
+
return model
|
| 989 |
+
|
| 990 |
+
|
| 991 |
+
def vit_giant2(patch_size=14, **kwargs):
|
| 992 |
+
model = VisionTransformer(
|
| 993 |
+
patch_size=patch_size,
|
| 994 |
+
embed_dim=1536,
|
| 995 |
+
depth=40,
|
| 996 |
+
num_heads=24,
|
| 997 |
+
mlp_ratio=4,
|
| 998 |
+
block_fn=functools.partial(Block, attn_class=MemEffAttention),
|
| 999 |
+
num_register_tokens=1,
|
| 1000 |
+
**kwargs,
|
| 1001 |
+
)
|
| 1002 |
+
return model
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4e69fd95fb0ce676f793355d1a7a53fb90145608098a2a64e2af2a324e6c20bc
|
| 3 |
+
size 1951820792
|
modeling_tips.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""TIPSv2 model for HuggingFace — wraps vision and text encoders."""
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
from typing import List, Optional, Union
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from transformers import PreTrainedModel
|
| 8 |
+
from transformers.utils import cached_file
|
| 9 |
+
|
| 10 |
+
from .configuration_tips import TIPSv2Config
|
| 11 |
+
from .image_encoder import vit_base, vit_giant2, vit_large, vit_small, vit_so400m
|
| 12 |
+
from .text_encoder import TextEncoder, Tokenizer
|
| 13 |
+
|
| 14 |
+
_VISION_FACTORIES = {
|
| 15 |
+
"vit_small": vit_small,
|
| 16 |
+
"vit_base": vit_base,
|
| 17 |
+
"vit_large": vit_large,
|
| 18 |
+
"vit_so400m": vit_so400m,
|
| 19 |
+
"vit_giant2": vit_giant2,
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@dataclass
|
| 24 |
+
class TIPSv2ImageOutput:
|
| 25 |
+
"""Output from the vision encoder."""
|
| 26 |
+
cls_token: torch.Tensor # (B, 1, D)
|
| 27 |
+
register_tokens: torch.Tensor # (B, R, D)
|
| 28 |
+
patch_tokens: torch.Tensor # (B, N, D)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@dataclass
|
| 32 |
+
class TIPSv2Output:
|
| 33 |
+
"""Output from the full model."""
|
| 34 |
+
image_features: Optional[TIPSv2ImageOutput] = None
|
| 35 |
+
text_embeds: Optional[torch.Tensor] = None
|
| 36 |
+
temperature: Optional[float] = None
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class TIPSv2Model(PreTrainedModel):
|
| 40 |
+
"""TIPSv2 vision-language model.
|
| 41 |
+
|
| 42 |
+
Usage::
|
| 43 |
+
|
| 44 |
+
model = AutoModel.from_pretrained("google/tipsv2-b14", trust_remote_code=True)
|
| 45 |
+
|
| 46 |
+
# Image features
|
| 47 |
+
out = model.encode_image(pixel_values) # pixel_values in [0, 1]
|
| 48 |
+
cls = out.cls_token # (B, 1, D)
|
| 49 |
+
spatial = out.patch_tokens # (B, N, D)
|
| 50 |
+
|
| 51 |
+
# Text features
|
| 52 |
+
text_emb = model.encode_text(["a photo of a cat"]) # (B, D)
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
config_class = TIPSv2Config
|
| 56 |
+
_no_split_modules = []
|
| 57 |
+
_supports_cache_class = False
|
| 58 |
+
_tied_weights_keys = []
|
| 59 |
+
|
| 60 |
+
@property
|
| 61 |
+
def all_tied_weights_keys(self):
|
| 62 |
+
return {}
|
| 63 |
+
|
| 64 |
+
def __init__(self, config: TIPSv2Config):
|
| 65 |
+
super().__init__(config)
|
| 66 |
+
|
| 67 |
+
self.vision_encoder = _VISION_FACTORIES[config.vision_fn](
|
| 68 |
+
img_size=config.img_size,
|
| 69 |
+
patch_size=config.patch_size,
|
| 70 |
+
ffn_layer=config.ffn_layer,
|
| 71 |
+
block_chunks=0,
|
| 72 |
+
init_values=config.init_values,
|
| 73 |
+
interpolate_antialias=True,
|
| 74 |
+
interpolate_offset=0.0,
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
self.text_encoder = TextEncoder(
|
| 78 |
+
config={
|
| 79 |
+
"hidden_size": config.text_hidden_size,
|
| 80 |
+
"mlp_dim": config.text_mlp_dim,
|
| 81 |
+
"num_heads": config.text_num_heads,
|
| 82 |
+
"num_layers": config.text_num_layers,
|
| 83 |
+
},
|
| 84 |
+
vocab_size=config.vocab_size,
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
self._tokenizer = None
|
| 88 |
+
|
| 89 |
+
def _load_tokenizer(self):
|
| 90 |
+
"""Load the SentencePiece tokenizer shipped with the checkpoint."""
|
| 91 |
+
return Tokenizer(cached_file(self.name_or_path, "tokenizer.model"))
|
| 92 |
+
|
| 93 |
+
@torch.no_grad()
|
| 94 |
+
def encode_image(self, pixel_values: torch.Tensor) -> TIPSv2ImageOutput:
|
| 95 |
+
"""Encode images. pixel_values: (B, 3, H, W) in [0, 1]."""
|
| 96 |
+
pixel_values = pixel_values.to(self.device)
|
| 97 |
+
cls_token, register_tokens, patch_tokens = self.vision_encoder(pixel_values)
|
| 98 |
+
return TIPSv2ImageOutput(
|
| 99 |
+
cls_token=cls_token,
|
| 100 |
+
register_tokens=register_tokens,
|
| 101 |
+
patch_tokens=patch_tokens,
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
@torch.no_grad()
|
| 105 |
+
def encode_text(
|
| 106 |
+
self,
|
| 107 |
+
texts: Union[str, List[str], torch.Tensor],
|
| 108 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 109 |
+
) -> torch.Tensor:
|
| 110 |
+
"""Encode text. Pass strings (auto-tokenized) or pre-tokenized tensors."""
|
| 111 |
+
if isinstance(texts, (str, list)):
|
| 112 |
+
if isinstance(texts, str):
|
| 113 |
+
texts = [texts]
|
| 114 |
+
if self._tokenizer is None:
|
| 115 |
+
self._tokenizer = self._load_tokenizer()
|
| 116 |
+
ids, paddings = self._tokenizer.tokenize(texts, max_len=self.config.max_len)
|
| 117 |
+
ids = torch.from_numpy(ids).to(self.device)
|
| 118 |
+
padding_mask = torch.from_numpy(paddings).to(self.device)
|
| 119 |
+
else:
|
| 120 |
+
ids = texts.to(self.device)
|
| 121 |
+
padding_mask = padding_mask.to(self.device)
|
| 122 |
+
return self.text_encoder(ids, padding_mask)
|
| 123 |
+
|
| 124 |
+
def forward(
|
| 125 |
+
self,
|
| 126 |
+
pixel_values: Optional[torch.Tensor] = None,
|
| 127 |
+
input_ids: Optional[torch.Tensor] = None,
|
| 128 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 129 |
+
) -> TIPSv2Output:
|
| 130 |
+
"""Forward pass for both or either modality."""
|
| 131 |
+
image_features = None
|
| 132 |
+
text_embeds = None
|
| 133 |
+
if pixel_values is not None:
|
| 134 |
+
image_features = self.encode_image(pixel_values)
|
| 135 |
+
if input_ids is not None:
|
| 136 |
+
text_embeds = self.encode_text(input_ids, padding_mask)
|
| 137 |
+
return TIPSv2Output(
|
| 138 |
+
image_features=image_features,
|
| 139 |
+
text_embeds=text_embeds,
|
| 140 |
+
temperature=self.config.temperature,
|
| 141 |
+
)
|
processor_config.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"image_processor": {
|
| 3 |
+
"do_convert_rgb": true,
|
| 4 |
+
"do_normalize": false,
|
| 5 |
+
"do_rescale": true,
|
| 6 |
+
"do_resize": true,
|
| 7 |
+
"image_processor_type": "Tipsv2ImageProcessor",
|
| 8 |
+
"resample": 2,
|
| 9 |
+
"rescale_factor": 0.00392156862745098,
|
| 10 |
+
"size": {
|
| 11 |
+
"height": 448,
|
| 12 |
+
"width": 448
|
| 13 |
+
}
|
| 14 |
+
},
|
| 15 |
+
"processor_class": "Tipsv2Processor"
|
| 16 |
+
}
|
text_encoder.py
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 Google LLC
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
# ==============================================================================
|
| 15 |
+
|
| 16 |
+
"""Text encoder implementation in PyTorch."""
|
| 17 |
+
|
| 18 |
+
import typing as t
|
| 19 |
+
|
| 20 |
+
import numpy as np
|
| 21 |
+
import sentencepiece as spm
|
| 22 |
+
import torch
|
| 23 |
+
from torch import nn
|
| 24 |
+
import torch.nn.functional as F
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class Tokenizer(object):
|
| 28 |
+
"""A simple tokenizer using SentencePiece."""
|
| 29 |
+
|
| 30 |
+
def __init__(self, tokenizer_path: str):
|
| 31 |
+
self.sp = spm.SentencePieceProcessor(model_file=tokenizer_path)
|
| 32 |
+
# Explicitly disable BOS/EOS to match the reference Colab implementation.
|
| 33 |
+
self._add_bos = False
|
| 34 |
+
self._add_eos = False
|
| 35 |
+
|
| 36 |
+
def tokenize(self, input_texts, max_len=64):
|
| 37 |
+
if isinstance(input_texts, str):
|
| 38 |
+
input_texts = [input_texts]
|
| 39 |
+
batch_ids = [
|
| 40 |
+
self.sp.encode(t.lower(), add_bos=self._add_bos, add_eos=self._add_eos)
|
| 41 |
+
for t in input_texts
|
| 42 |
+
]
|
| 43 |
+
tokens = np.zeros((len(batch_ids), max_len), dtype=np.int64)
|
| 44 |
+
for i, ids in enumerate(batch_ids):
|
| 45 |
+
length = min(len(ids), max_len)
|
| 46 |
+
tokens[i, :length] = ids[:length]
|
| 47 |
+
is_padding = (tokens == 0).astype(np.int32)
|
| 48 |
+
return tokens, is_padding
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class PositionalEmbedding(nn.Module):
|
| 52 |
+
"""Generates position embedding for a given 1-d sequence.
|
| 53 |
+
|
| 54 |
+
Attributes:
|
| 55 |
+
min_timescale: Start of the geometric index. Determines the periodicity of
|
| 56 |
+
the added signal.
|
| 57 |
+
max_timescale: End of the geometric index. Determines the frequency of the
|
| 58 |
+
added signal.
|
| 59 |
+
embedding_dim: Dimension of the embedding to be generated.
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
min_timescale: int = 1
|
| 63 |
+
max_timescale: int = 10_000
|
| 64 |
+
embedding_dim: int = 0
|
| 65 |
+
|
| 66 |
+
def __init__(self, embedding_dim: int):
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.embedding_dim = embedding_dim
|
| 69 |
+
|
| 70 |
+
def __call__(self, seq_length: int = None, position: torch.tensor = None):
|
| 71 |
+
"""Generates a torch.tensor of sinusoids with different frequencies.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
seq_length: an optional Python int defining the output sequence length.
|
| 75 |
+
if the `position` argument is specified.
|
| 76 |
+
position: [B, seq_length], optional position for each token in the
|
| 77 |
+
sequence, only required when the sequence is packed.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
[B, seqlen, D] if `position` is specified, else [1, seqlen, D]
|
| 81 |
+
"""
|
| 82 |
+
if position is None:
|
| 83 |
+
assert seq_length is not None
|
| 84 |
+
# [1, seqlen]
|
| 85 |
+
position = torch.arange(seq_length, dtype=torch.float32)[None, :]
|
| 86 |
+
else:
|
| 87 |
+
assert position.ndim == 2, position.shape
|
| 88 |
+
|
| 89 |
+
num_timescales = self.embedding_dim // 2
|
| 90 |
+
log_timescale_increment = torch.log(
|
| 91 |
+
torch.tensor(float(self.max_timescale) / float(self.min_timescale))
|
| 92 |
+
) / torch.maximum(
|
| 93 |
+
torch.tensor(num_timescales, dtype=torch.float32) - 1, torch.tensor(1)
|
| 94 |
+
)
|
| 95 |
+
inv_timescales = self.min_timescale * torch.exp(
|
| 96 |
+
torch.arange(num_timescales, dtype=torch.float32)
|
| 97 |
+
* -log_timescale_increment
|
| 98 |
+
)
|
| 99 |
+
scaled_time = position[:, :, None] * inv_timescales[None, None, :]
|
| 100 |
+
signal = torch.cat((torch.sin(scaled_time), torch.cos(scaled_time)), dim=2)
|
| 101 |
+
# Force usage of `np` rather than `jnp` to compute static values at trace
|
| 102 |
+
# time.
|
| 103 |
+
signal = F.pad(signal, (0, self.embedding_dim % 2, 0, 0, 0, 0))
|
| 104 |
+
return signal
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class MlpBlockWithMask(nn.Module):
|
| 108 |
+
"""Transformer MLP / feed-forward block that supports masking."""
|
| 109 |
+
|
| 110 |
+
def __init__(
|
| 111 |
+
self,
|
| 112 |
+
mlp_dim: int,
|
| 113 |
+
d_model: int,
|
| 114 |
+
use_bias: bool = True,
|
| 115 |
+
dtype: torch.dtype = torch.float32,
|
| 116 |
+
activation_fn: nn.Module = nn.GELU,
|
| 117 |
+
):
|
| 118 |
+
super().__init__()
|
| 119 |
+
|
| 120 |
+
self.mlp_dim = mlp_dim
|
| 121 |
+
self.d_model = d_model
|
| 122 |
+
self.use_bias = use_bias
|
| 123 |
+
self.dtype = dtype
|
| 124 |
+
self.activation_fn = activation_fn
|
| 125 |
+
|
| 126 |
+
self.c_fc = nn.Linear(
|
| 127 |
+
in_features=self.d_model,
|
| 128 |
+
out_features=self.mlp_dim,
|
| 129 |
+
dtype=self.dtype,
|
| 130 |
+
bias=self.use_bias,
|
| 131 |
+
)
|
| 132 |
+
self.c_proj = nn.Linear(
|
| 133 |
+
in_features=self.mlp_dim,
|
| 134 |
+
out_features=self.d_model,
|
| 135 |
+
dtype=self.dtype,
|
| 136 |
+
bias=self.use_bias,
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
def __call__(
|
| 140 |
+
self, inputs: torch.Tensor, mlp_mask: torch.Tensor
|
| 141 |
+
) -> torch.Tensor:
|
| 142 |
+
"""Applies Transformer MlpBlock with mask module."""
|
| 143 |
+
x = self.c_fc(inputs)
|
| 144 |
+
x = self.activation_fn()(x)
|
| 145 |
+
x = x * mlp_mask[..., None] # First masking.
|
| 146 |
+
x = self.c_proj(x)
|
| 147 |
+
x = x * mlp_mask[..., None] # Second masking.
|
| 148 |
+
return x
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class ResidualAttentionBlock(nn.Module):
|
| 152 |
+
"""Transformer residual attention block."""
|
| 153 |
+
|
| 154 |
+
def __init__(
|
| 155 |
+
self,
|
| 156 |
+
d_model: int,
|
| 157 |
+
n_head: int,
|
| 158 |
+
mlp_dim: int,
|
| 159 |
+
dtype: torch.dtype = torch.float32,
|
| 160 |
+
):
|
| 161 |
+
super().__init__()
|
| 162 |
+
self.d_model = d_model
|
| 163 |
+
self.n_head = n_head
|
| 164 |
+
self.mlp_dim = mlp_dim
|
| 165 |
+
self.dtype = dtype
|
| 166 |
+
|
| 167 |
+
self.attn = nn.MultiheadAttention(d_model, n_head, dtype=self.dtype)
|
| 168 |
+
self.ln_1 = nn.LayerNorm(d_model, dtype=self.dtype)
|
| 169 |
+
self.mlp = MlpBlockWithMask(
|
| 170 |
+
self.mlp_dim,
|
| 171 |
+
d_model,
|
| 172 |
+
use_bias=True,
|
| 173 |
+
dtype=self.dtype,
|
| 174 |
+
activation_fn=nn.ReLU,
|
| 175 |
+
)
|
| 176 |
+
self.ln_2 = nn.LayerNorm(d_model, dtype=self.dtype)
|
| 177 |
+
|
| 178 |
+
def attention(self, x: torch.Tensor, mask: torch.Tensor):
|
| 179 |
+
attn_mask = (
|
| 180 |
+
mask[:, None, None, :]
|
| 181 |
+
.repeat(1, self.n_head, x.shape[0], 1)
|
| 182 |
+
.flatten(0, 1)
|
| 183 |
+
)
|
| 184 |
+
attn_mask[attn_mask == 0] = float('-inf')
|
| 185 |
+
attn_mask[attn_mask == 1] = 0
|
| 186 |
+
return self.attn(x, x, x, need_weights=False, attn_mask=attn_mask)[0]
|
| 187 |
+
|
| 188 |
+
def forward(self, x: torch.Tensor, mask: torch.Tensor):
|
| 189 |
+
x = x + self.attention(self.ln_1(x), mask.permute(1, 0))
|
| 190 |
+
x = x + self.mlp(self.ln_2(x), mask)
|
| 191 |
+
return x, mask
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
class SequentialMultiInput(nn.Sequential):
|
| 195 |
+
"""Sequential module that can take multiple inputs."""
|
| 196 |
+
|
| 197 |
+
def forward(self, *inputs):
|
| 198 |
+
for module in self._modules.values():
|
| 199 |
+
if isinstance(inputs, tuple):
|
| 200 |
+
inputs = module(*inputs)
|
| 201 |
+
else:
|
| 202 |
+
inputs = module(inputs)
|
| 203 |
+
return inputs
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class Transformer(nn.Module):
|
| 207 |
+
"""Transformer implementation."""
|
| 208 |
+
|
| 209 |
+
def __init__(
|
| 210 |
+
self,
|
| 211 |
+
width: int,
|
| 212 |
+
layers: int,
|
| 213 |
+
heads: int,
|
| 214 |
+
mlp_dim: int,
|
| 215 |
+
dtype: torch.dtype = torch.float32,
|
| 216 |
+
):
|
| 217 |
+
super().__init__()
|
| 218 |
+
self.width = width
|
| 219 |
+
self.layers = layers
|
| 220 |
+
self.heads = heads
|
| 221 |
+
self.mlp_dim = mlp_dim
|
| 222 |
+
self.dtype = dtype
|
| 223 |
+
|
| 224 |
+
self.resblocks = SequentialMultiInput(*[
|
| 225 |
+
ResidualAttentionBlock(self.width, self.heads, self.mlp_dim, self.dtype)
|
| 226 |
+
for _ in range(self.layers)
|
| 227 |
+
])
|
| 228 |
+
|
| 229 |
+
def forward(self, x: torch.Tensor, mask: torch.Tensor):
|
| 230 |
+
return self.resblocks(x, mask)[0]
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
class GlobalAvgPooling(nn.Module):
|
| 234 |
+
"""Performs a simple global pooling over the input with optional paddings.
|
| 235 |
+
|
| 236 |
+
Attributes:
|
| 237 |
+
pooling_dims: A list of dims to perform pooling over.
|
| 238 |
+
keepdims: If True, keep dimension of inputs after pooling.
|
| 239 |
+
"""
|
| 240 |
+
|
| 241 |
+
pooling_dims: t.Sequence[int]
|
| 242 |
+
epsilon: float = 1e-8
|
| 243 |
+
|
| 244 |
+
def __init__(
|
| 245 |
+
self, pooling_dims: t.Sequence[int], epsilon: float = 1e-8
|
| 246 |
+
):
|
| 247 |
+
super().__init__()
|
| 248 |
+
self.pooling_dims = pooling_dims
|
| 249 |
+
self.epsilon = epsilon
|
| 250 |
+
|
| 251 |
+
if not all([p_dims >= 0 for p_dims in self.pooling_dims]):
|
| 252 |
+
raise ValueError('pooling_dims must be non-negative integers.')
|
| 253 |
+
|
| 254 |
+
def __call__(
|
| 255 |
+
self,
|
| 256 |
+
inputs: torch.tensor,
|
| 257 |
+
compatible_paddings: torch.tensor,
|
| 258 |
+
):
|
| 259 |
+
"""Applies global average spatial pooling to inputs.
|
| 260 |
+
|
| 261 |
+
Args:
|
| 262 |
+
inputs: An input tensor.
|
| 263 |
+
compatible_paddings: paddings of inputs with shapes compatible with
|
| 264 |
+
inputs, e.g. compatible_paddings with shape [B, 1] for inputs with shape
|
| 265 |
+
[B, D].
|
| 266 |
+
|
| 267 |
+
Returns:
|
| 268 |
+
Output tensor with global pooling applied.
|
| 269 |
+
"""
|
| 270 |
+
padded_value = torch.zeros_like(inputs)
|
| 271 |
+
padded_value = torch.ones_like(inputs) * padded_value
|
| 272 |
+
inputs = torch.where(compatible_paddings > 0, padded_value, inputs)
|
| 273 |
+
valid_inputs = (
|
| 274 |
+
torch.sum(
|
| 275 |
+
1.0 - compatible_paddings,
|
| 276 |
+
self.pooling_dims,
|
| 277 |
+
keepdims=True,
|
| 278 |
+
dtype=inputs.dtype,
|
| 279 |
+
)
|
| 280 |
+
+ self.epsilon
|
| 281 |
+
)
|
| 282 |
+
inputs_sum = torch.sum(inputs, self.pooling_dims, keepdims=True)
|
| 283 |
+
outputs = torch.divide(inputs_sum, valid_inputs).type(inputs.dtype)
|
| 284 |
+
outputs = torch.squeeze(outputs, axis=self.pooling_dims)
|
| 285 |
+
return outputs
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
class TextEncoder(nn.Module):
|
| 289 |
+
"""Text encoder implementation."""
|
| 290 |
+
|
| 291 |
+
def __init__(
|
| 292 |
+
self,
|
| 293 |
+
config: t.Dict[str, int],
|
| 294 |
+
vocab_size: int,
|
| 295 |
+
dtype: torch.dtype = torch.float32,
|
| 296 |
+
scale_sqrt_depth: bool = True,
|
| 297 |
+
):
|
| 298 |
+
super().__init__()
|
| 299 |
+
self.vocab_size = vocab_size
|
| 300 |
+
self.dtype = dtype
|
| 301 |
+
self.scale_sqrt_depth = scale_sqrt_depth
|
| 302 |
+
|
| 303 |
+
# The text tower layers are fixed independent of vision tower size.
|
| 304 |
+
self.transformer_layers = config['num_layers']
|
| 305 |
+
self.embedding_dim = config['hidden_size']
|
| 306 |
+
self.transformer_width = config['hidden_size']
|
| 307 |
+
self.mlp_dim = config['mlp_dim']
|
| 308 |
+
self.transformer_heads = config['num_heads']
|
| 309 |
+
|
| 310 |
+
self.token_embedding = nn.Embedding(
|
| 311 |
+
self.vocab_size, self.embedding_dim, dtype=self.dtype
|
| 312 |
+
)
|
| 313 |
+
self.pos_embedder = PositionalEmbedding(embedding_dim=self.embedding_dim)
|
| 314 |
+
self.transformer = Transformer(
|
| 315 |
+
width=self.transformer_width,
|
| 316 |
+
layers=self.transformer_layers,
|
| 317 |
+
heads=self.transformer_heads,
|
| 318 |
+
mlp_dim=self.mlp_dim,
|
| 319 |
+
dtype=self.dtype,
|
| 320 |
+
)
|
| 321 |
+
self.pooling = GlobalAvgPooling(pooling_dims=[1])
|
| 322 |
+
self.ln_final = nn.LayerNorm(self.transformer_width, dtype=self.dtype)
|
| 323 |
+
|
| 324 |
+
def __call__(
|
| 325 |
+
self,
|
| 326 |
+
ids: torch.tensor,
|
| 327 |
+
paddings: torch.tensor,
|
| 328 |
+
):
|
| 329 |
+
"""Applies TextEncoder module."""
|
| 330 |
+
_, seq_length = ids.shape
|
| 331 |
+
mask = (paddings == 0).type(torch.float32)
|
| 332 |
+
mask = mask.permute(1, 0) # NL -> LN
|
| 333 |
+
x = self.token_embedding(ids)
|
| 334 |
+
if self.scale_sqrt_depth:
|
| 335 |
+
x = x * (self.embedding_dim**0.5)
|
| 336 |
+
x = x + self.pos_embedder(seq_length=seq_length).to(x.device)
|
| 337 |
+
x = x.permute(1, 0, 2) # NLD -> LND
|
| 338 |
+
x = self.transformer(x, mask)
|
| 339 |
+
x = x.permute(1, 0, 2) # LND -> NLD
|
| 340 |
+
x = self.ln_final(x)
|
| 341 |
+
x = self.pooling(x, compatible_paddings=paddings[:, :, None])
|
| 342 |
+
return x
|
tokenizer.model
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4c40e7723348d5d9a3d3c2bdcec5120d97fb29edfe1bf118b4494bce02fc7624
|
| 3 |
+
size 731655
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"backend": "tokenizers",
|
| 3 |
+
"bos_token": null,
|
| 4 |
+
"do_lower_case": true,
|
| 5 |
+
"eos_token": null,
|
| 6 |
+
"model_max_length": 64,
|
| 7 |
+
"pad_token": "<pad>",
|
| 8 |
+
"processor_class": "Tipsv2Processor",
|
| 9 |
+
"token_type_ids_pattern": "all_zeros",
|
| 10 |
+
"tokenizer_class": "Tipsv2Tokenizer",
|
| 11 |
+
"unk_token": "<unk>"
|
| 12 |
+
}
|