Zero-Shot Image Classification
Transformers
Safetensors
tipsv2
feature-extraction
vision
image-text
contrastive-learning
zero-shot
custom_code
Instructions to use invincible-jha/tipsv2-g14 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use invincible-jha/tipsv2-g14 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-image-classification", model="invincible-jha/tipsv2-g14", 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("invincible-jha/tipsv2-g14", trust_remote_code=True) model = AutoModel.from_pretrained("invincible-jha/tipsv2-g14", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Commit ·
b9e419d
0
Parent(s):
Duplicate from google/tipsv2-g14
Browse filesCo-authored-by: Gabriele Berton <gberton@users.noreply.huggingface.co>
- .gitattributes +35 -0
- README.md +122 -0
- config.json +24 -0
- configuration_tips.py +46 -0
- image_encoder.py +1002 -0
- model.safetensors +3 -0
- modeling_tips.py +161 -0
- text_encoder.py +344 -0
- tokenizer.model +3 -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,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
tags:
|
| 4 |
+
- vision
|
| 5 |
+
- image-text
|
| 6 |
+
- contrastive-learning
|
| 7 |
+
- zero-shot
|
| 8 |
+
- feature-extraction
|
| 9 |
+
library_name: transformers
|
| 10 |
+
pipeline_tag: zero-shot-image-classification
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# TIPSv2 — g/14
|
| 14 |
+
|
| 15 |
+
TIPSv2 (Text-Image Pre-training with Spatial awareness) is a family of contrastive vision-language models that produce spatially rich image features aligned with text embeddings. This is the Giant variant with 1.1B vision params and 389M text params. Try the code snippets below or check out the [GitHub repo](https://github.com/google-deepmind/tips) for more use cases and visualizations, including zero-shot segmentation.
|
| 16 |
+
|
| 17 |
+
| Variant | Vision params | Text params | Embed dim | DPT Heads |
|
| 18 |
+
|---------|--------------|-------------|-----------|-----------|
|
| 19 |
+
| [B/14](https://huggingface.co/google/tipsv2-b14) | 86M | 110M | 768 | [B/14-dpt](https://huggingface.co/google/tipsv2-b14-dpt) |
|
| 20 |
+
| [L/14](https://huggingface.co/google/tipsv2-l14) | 303M | 184M | 1024 | [L/14-dpt](https://huggingface.co/google/tipsv2-l14-dpt) |
|
| 21 |
+
| [SO400m/14](https://huggingface.co/google/tipsv2-so400m14) | 412M | 448M | 1152 | [SO400m/14-dpt](https://huggingface.co/google/tipsv2-so400m14-dpt) |
|
| 22 |
+
| [g/14](https://huggingface.co/google/tipsv2-g14) | 1.1B | 389M | 1536 | [g/14-dpt](https://huggingface.co/google/tipsv2-g14-dpt) |
|
| 23 |
+
|
| 24 |
+
## Usage
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
pip install transformers torch torchvision sentencepiece scikit-learn
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
### Load the model
|
| 31 |
+
|
| 32 |
+
```python
|
| 33 |
+
from transformers import AutoModel
|
| 34 |
+
|
| 35 |
+
model = AutoModel.from_pretrained("google/tipsv2-g14", trust_remote_code=True)
|
| 36 |
+
model.eval()
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
### Encode images
|
| 40 |
+
|
| 41 |
+
Images should be tensors in `[0, 1]` range (just `ToTensor()`, no ImageNet normalization).
|
| 42 |
+
|
| 43 |
+
```python
|
| 44 |
+
from torchvision import transforms
|
| 45 |
+
from PIL import Image
|
| 46 |
+
import requests
|
| 47 |
+
|
| 48 |
+
transform = transforms.Compose([
|
| 49 |
+
transforms.Resize((448, 448)),
|
| 50 |
+
transforms.ToTensor(),
|
| 51 |
+
])
|
| 52 |
+
|
| 53 |
+
url = "https://huggingface.co/spaces/google/TIPSv2/resolve/main/examples/zeroseg/pascal_context_00049_image.png"
|
| 54 |
+
image = Image.open(requests.get(url, stream=True).raw)
|
| 55 |
+
pixel_values = transform(image).unsqueeze(0)
|
| 56 |
+
out = model.encode_image(pixel_values)
|
| 57 |
+
|
| 58 |
+
print(out.cls_token.shape) # (1, 1, 1536) — global image embedding
|
| 59 |
+
print(out.patch_tokens.shape) # (1, 1024, 1536) — per-patch spatial features
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
### Encode text
|
| 63 |
+
|
| 64 |
+
```python
|
| 65 |
+
text_emb = model.encode_text(["a photo of a bus", "a photo of a dog"])
|
| 66 |
+
print(text_emb.shape) # (2, 1536) — one embedding per query
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
### Zero-shot classification
|
| 70 |
+
|
| 71 |
+
```python
|
| 72 |
+
import torch.nn.functional as F
|
| 73 |
+
|
| 74 |
+
classes = ["bus", "car", "dog", "cat"]
|
| 75 |
+
cls = F.normalize(out.cls_token[:, 0, :], dim=-1)
|
| 76 |
+
text_emb = F.normalize(model.encode_text(classes), dim=-1)
|
| 77 |
+
similarity = cls @ text_emb.T
|
| 78 |
+
print(classes[similarity.argmax()]) # bus — predicted class
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
### Visualize spatial features
|
| 82 |
+
|
| 83 |
+
```python
|
| 84 |
+
import numpy as np
|
| 85 |
+
from sklearn.decomposition import PCA
|
| 86 |
+
|
| 87 |
+
spatial = out.patch_tokens.reshape(1, 32, 32, 1536)
|
| 88 |
+
feat = spatial[0].detach().cpu().numpy().reshape(-1, 1536)
|
| 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 |
+
print(rgb.shape) # (32, 32, 3) — PCA of patch features as RGB
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
### GPU inference
|
| 95 |
+
|
| 96 |
+
```python
|
| 97 |
+
model = model.cuda()
|
| 98 |
+
out = model.encode_image(pixel_values.cuda())
|
| 99 |
+
text_emb = model.encode_text(["a city"])
|
| 100 |
+
```
|
| 101 |
+
|
| 102 |
+
## Model details
|
| 103 |
+
|
| 104 |
+
- **Architecture**: ViT vision encoder (40 layers) + Transformer text encoder (12 layers)
|
| 105 |
+
- **Image preprocessing**: resize to any resolution, convert to `[0, 1]` (no ImageNet normalization)
|
| 106 |
+
- **Text preprocessing**: SentencePiece tokenizer, lowercased, max 64 tokens
|
| 107 |
+
- **Patch size**: 14x14 pixels
|
| 108 |
+
|
| 109 |
+
## License
|
| 110 |
+
|
| 111 |
+
Apache 2.0
|
| 112 |
+
|
| 113 |
+
## Citation
|
| 114 |
+
|
| 115 |
+
```bibtex
|
| 116 |
+
@inproceedings{cao2026tipsv2,
|
| 117 |
+
title = {{TIPSv2: Advancing Vision-Language Pretraining with Enhanced Patch-Text Alignment}},
|
| 118 |
+
author = {Cao, Bingyi and Chen, Koert and Maninis, Kevis-Kokitsi and Chen, Kaifeng and Karpur, Arjun and Xia, Ye and Dua, Sahil and Dabral, Tanmaya and Han, Guangxing and Han, Bohyung and Ainslie, Joshua and Bewley, Alex and Jacob, Mithun and Wagner, Rene and Ramos, Washington and Choromanski, Krzysztof and Seyedhosseini, Mojtaba and Zhou, Howard and Araujo, Andre},
|
| 119 |
+
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
|
| 120 |
+
year = {2026}
|
| 121 |
+
}
|
| 122 |
+
```
|
config.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "tipsv2",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"TIPSv2Model"
|
| 5 |
+
],
|
| 6 |
+
"auto_map": {
|
| 7 |
+
"AutoConfig": "configuration_tips.TIPSv2Config",
|
| 8 |
+
"AutoModel": "modeling_tips.TIPSv2Model"
|
| 9 |
+
},
|
| 10 |
+
"patch_size": 14,
|
| 11 |
+
"img_size": 448,
|
| 12 |
+
"init_values": 1.0,
|
| 13 |
+
"num_register_tokens": 1,
|
| 14 |
+
"vocab_size": 32000,
|
| 15 |
+
"max_len": 64,
|
| 16 |
+
"vision_fn": "vit_giant2",
|
| 17 |
+
"embed_dim": 1536,
|
| 18 |
+
"text_hidden_size": 1536,
|
| 19 |
+
"text_mlp_dim": 6144,
|
| 20 |
+
"text_num_heads": 24,
|
| 21 |
+
"text_num_layers": 12,
|
| 22 |
+
"ffn_layer": "swiglu",
|
| 23 |
+
"temperature": 0.003622700460255146
|
| 24 |
+
}
|
configuration_tips.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""TIPSv2 model configuration."""
|
| 2 |
+
|
| 3 |
+
from transformers import PretrainedConfig
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class TIPSv2Config(PretrainedConfig):
|
| 7 |
+
"""Configuration for TIPSv2 vision-language model."""
|
| 8 |
+
|
| 9 |
+
model_type = "tipsv2"
|
| 10 |
+
|
| 11 |
+
def __init__(
|
| 12 |
+
self,
|
| 13 |
+
# Vision encoder
|
| 14 |
+
vision_fn="vit_base",
|
| 15 |
+
embed_dim=768,
|
| 16 |
+
patch_size=14,
|
| 17 |
+
img_size=448,
|
| 18 |
+
ffn_layer="mlp",
|
| 19 |
+
init_values=1.0,
|
| 20 |
+
num_register_tokens=1,
|
| 21 |
+
# Text encoder
|
| 22 |
+
text_hidden_size=768,
|
| 23 |
+
text_mlp_dim=3072,
|
| 24 |
+
text_num_heads=12,
|
| 25 |
+
text_num_layers=12,
|
| 26 |
+
vocab_size=32000,
|
| 27 |
+
max_len=64,
|
| 28 |
+
# Contrastive
|
| 29 |
+
temperature=0.01,
|
| 30 |
+
**kwargs,
|
| 31 |
+
):
|
| 32 |
+
super().__init__(**kwargs)
|
| 33 |
+
self.vision_fn = vision_fn
|
| 34 |
+
self.embed_dim = embed_dim
|
| 35 |
+
self.patch_size = patch_size
|
| 36 |
+
self.img_size = img_size
|
| 37 |
+
self.ffn_layer = ffn_layer
|
| 38 |
+
self.init_values = init_values
|
| 39 |
+
self.num_register_tokens = num_register_tokens
|
| 40 |
+
self.text_hidden_size = text_hidden_size
|
| 41 |
+
self.text_mlp_dim = text_mlp_dim
|
| 42 |
+
self.text_num_heads = text_num_heads
|
| 43 |
+
self.text_num_layers = text_num_layers
|
| 44 |
+
self.vocab_size = vocab_size
|
| 45 |
+
self.max_len = max_len
|
| 46 |
+
self.temperature = temperature
|
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:c31c27837b177bbb6cc4d7234a01476e357fdb0bbd9d6584b4c5b0dd9b4ebfe6
|
| 3 |
+
size 6100424272
|
modeling_tips.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""TIPSv2 model for HuggingFace — wraps vision and text encoders."""
|
| 2 |
+
|
| 3 |
+
import importlib
|
| 4 |
+
import os
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import List, Optional, Union
|
| 8 |
+
|
| 9 |
+
import numpy as np
|
| 10 |
+
import torch
|
| 11 |
+
from huggingface_hub import hf_hub_download
|
| 12 |
+
from transformers import PreTrainedModel
|
| 13 |
+
|
| 14 |
+
from .configuration_tips import TIPSv2Config
|
| 15 |
+
|
| 16 |
+
_this_dir = Path(__file__).parent
|
| 17 |
+
_sibling_cache = {}
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _load_sibling(name, repo_id=None):
|
| 21 |
+
"""Import a sibling .py from the same dir, downloading from HF if needed."""
|
| 22 |
+
if name in _sibling_cache:
|
| 23 |
+
return _sibling_cache[name]
|
| 24 |
+
path = _this_dir / f"{name}.py"
|
| 25 |
+
if not path.exists() and repo_id:
|
| 26 |
+
path = Path(hf_hub_download(repo_id, f"{name}.py"))
|
| 27 |
+
spec = importlib.util.spec_from_file_location(name, str(path))
|
| 28 |
+
mod = importlib.util.module_from_spec(spec)
|
| 29 |
+
spec.loader.exec_module(mod)
|
| 30 |
+
_sibling_cache[name] = mod
|
| 31 |
+
return mod
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@dataclass
|
| 35 |
+
class TIPSv2ImageOutput:
|
| 36 |
+
"""Output from the vision encoder."""
|
| 37 |
+
cls_token: torch.Tensor # (B, 1, D)
|
| 38 |
+
register_tokens: torch.Tensor # (B, R, D)
|
| 39 |
+
patch_tokens: torch.Tensor # (B, N, D)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@dataclass
|
| 43 |
+
class TIPSv2Output:
|
| 44 |
+
"""Output from the full model."""
|
| 45 |
+
image_features: Optional[TIPSv2ImageOutput] = None
|
| 46 |
+
text_embeds: Optional[torch.Tensor] = None
|
| 47 |
+
temperature: Optional[float] = None
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class TIPSv2Model(PreTrainedModel):
|
| 51 |
+
"""TIPSv2 vision-language model.
|
| 52 |
+
|
| 53 |
+
Usage::
|
| 54 |
+
|
| 55 |
+
model = AutoModel.from_pretrained("google/tipsv2-b14", trust_remote_code=True)
|
| 56 |
+
|
| 57 |
+
# Image features
|
| 58 |
+
out = model.encode_image(pixel_values) # pixel_values in [0, 1]
|
| 59 |
+
cls = out.cls_token # (B, 1, D)
|
| 60 |
+
spatial = out.patch_tokens # (B, N, D)
|
| 61 |
+
|
| 62 |
+
# Text features
|
| 63 |
+
text_emb = model.encode_text(["a photo of a cat"]) # (B, D)
|
| 64 |
+
"""
|
| 65 |
+
|
| 66 |
+
config_class = TIPSv2Config
|
| 67 |
+
_no_split_modules = []
|
| 68 |
+
_supports_cache_class = False
|
| 69 |
+
_tied_weights_keys = []
|
| 70 |
+
|
| 71 |
+
@property
|
| 72 |
+
def all_tied_weights_keys(self):
|
| 73 |
+
return {}
|
| 74 |
+
|
| 75 |
+
def __init__(self, config: TIPSv2Config):
|
| 76 |
+
super().__init__(config)
|
| 77 |
+
|
| 78 |
+
repo_id = getattr(config, "_name_or_path", None)
|
| 79 |
+
ie = _load_sibling("image_encoder", repo_id)
|
| 80 |
+
te = _load_sibling("text_encoder", repo_id)
|
| 81 |
+
|
| 82 |
+
build_fn = getattr(ie, config.vision_fn)
|
| 83 |
+
self.vision_encoder = build_fn(
|
| 84 |
+
img_size=config.img_size,
|
| 85 |
+
patch_size=config.patch_size,
|
| 86 |
+
ffn_layer=config.ffn_layer,
|
| 87 |
+
block_chunks=0,
|
| 88 |
+
init_values=config.init_values,
|
| 89 |
+
interpolate_antialias=True,
|
| 90 |
+
interpolate_offset=0.0,
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
self.text_encoder = te.TextEncoder(
|
| 94 |
+
config={
|
| 95 |
+
"hidden_size": config.text_hidden_size,
|
| 96 |
+
"mlp_dim": config.text_mlp_dim,
|
| 97 |
+
"num_heads": config.text_num_heads,
|
| 98 |
+
"num_layers": config.text_num_layers,
|
| 99 |
+
},
|
| 100 |
+
vocab_size=config.vocab_size,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
self._tokenizer = None
|
| 104 |
+
self._te_mod = te
|
| 105 |
+
|
| 106 |
+
def _load_tokenizer(self):
|
| 107 |
+
"""Lazy-load the SentencePiece tokenizer."""
|
| 108 |
+
tok_path = _this_dir / "tokenizer.model"
|
| 109 |
+
if not tok_path.exists():
|
| 110 |
+
tok_path = hf_hub_download(self.name_or_path, "tokenizer.model")
|
| 111 |
+
return self._te_mod.Tokenizer(str(tok_path))
|
| 112 |
+
|
| 113 |
+
@torch.no_grad()
|
| 114 |
+
def encode_image(self, pixel_values: torch.Tensor) -> TIPSv2ImageOutput:
|
| 115 |
+
"""Encode images. pixel_values: (B, 3, H, W) in [0, 1]."""
|
| 116 |
+
pixel_values = pixel_values.to(self.device)
|
| 117 |
+
cls_token, register_tokens, patch_tokens = self.vision_encoder(pixel_values)
|
| 118 |
+
return TIPSv2ImageOutput(
|
| 119 |
+
cls_token=cls_token,
|
| 120 |
+
register_tokens=register_tokens,
|
| 121 |
+
patch_tokens=patch_tokens,
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
@torch.no_grad()
|
| 125 |
+
def encode_text(
|
| 126 |
+
self,
|
| 127 |
+
texts: Union[str, List[str], torch.Tensor],
|
| 128 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 129 |
+
) -> torch.Tensor:
|
| 130 |
+
"""Encode text. Pass strings (auto-tokenized) or pre-tokenized tensors."""
|
| 131 |
+
if isinstance(texts, (str, list)):
|
| 132 |
+
if isinstance(texts, str):
|
| 133 |
+
texts = [texts]
|
| 134 |
+
if self._tokenizer is None:
|
| 135 |
+
self._tokenizer = self._load_tokenizer()
|
| 136 |
+
ids, paddings = self._tokenizer.tokenize(texts, max_len=self.config.max_len)
|
| 137 |
+
ids = torch.from_numpy(ids).to(self.device)
|
| 138 |
+
padding_mask = torch.from_numpy(paddings).to(self.device)
|
| 139 |
+
else:
|
| 140 |
+
ids = texts.to(self.device)
|
| 141 |
+
padding_mask = padding_mask.to(self.device)
|
| 142 |
+
return self.text_encoder(ids, padding_mask)
|
| 143 |
+
|
| 144 |
+
def forward(
|
| 145 |
+
self,
|
| 146 |
+
pixel_values: Optional[torch.Tensor] = None,
|
| 147 |
+
input_ids: Optional[torch.Tensor] = None,
|
| 148 |
+
padding_mask: Optional[torch.Tensor] = None,
|
| 149 |
+
) -> TIPSv2Output:
|
| 150 |
+
"""Forward pass for both or either modality."""
|
| 151 |
+
image_features = None
|
| 152 |
+
text_embeds = None
|
| 153 |
+
if pixel_values is not None:
|
| 154 |
+
image_features = self.encode_image(pixel_values)
|
| 155 |
+
if input_ids is not None:
|
| 156 |
+
text_embeds = self.encode_text(input_ids, padding_mask)
|
| 157 |
+
return TIPSv2Output(
|
| 158 |
+
image_features=image_features,
|
| 159 |
+
text_embeds=text_embeds,
|
| 160 |
+
temperature=self.config.temperature,
|
| 161 |
+
)
|
text_encoder.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
# Match tensorflow_text.SentencepieceTokenizer(add_bos=False, add_eos=False)
|
| 33 |
+
self.sp.SetEncodeExtraOptions("")
|
| 34 |
+
# Explicitly disable BOS/EOS to match the reference Colab implementation.
|
| 35 |
+
self._add_bos = False
|
| 36 |
+
self._add_eos = False
|
| 37 |
+
|
| 38 |
+
def tokenize(self, input_texts, max_len=64):
|
| 39 |
+
if isinstance(input_texts, str):
|
| 40 |
+
input_texts = [input_texts]
|
| 41 |
+
batch_ids = [
|
| 42 |
+
self.sp.encode(t.lower(), add_bos=self._add_bos, add_eos=self._add_eos)
|
| 43 |
+
for t in input_texts
|
| 44 |
+
]
|
| 45 |
+
tokens = np.zeros((len(batch_ids), max_len), dtype=np.int64)
|
| 46 |
+
for i, ids in enumerate(batch_ids):
|
| 47 |
+
length = min(len(ids), max_len)
|
| 48 |
+
tokens[i, :length] = ids[:length]
|
| 49 |
+
is_padding = (tokens == 0).astype(np.int32)
|
| 50 |
+
return tokens, is_padding
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class PositionalEmbedding(nn.Module):
|
| 54 |
+
"""Generates position embedding for a given 1-d sequence.
|
| 55 |
+
|
| 56 |
+
Attributes:
|
| 57 |
+
min_timescale: Start of the geometric index. Determines the periodicity of
|
| 58 |
+
the added signal.
|
| 59 |
+
max_timescale: End of the geometric index. Determines the frequency of the
|
| 60 |
+
added signal.
|
| 61 |
+
embedding_dim: Dimension of the embedding to be generated.
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
min_timescale: int = 1
|
| 65 |
+
max_timescale: int = 10_000
|
| 66 |
+
embedding_dim: int = 0
|
| 67 |
+
|
| 68 |
+
def __init__(self, embedding_dim: int):
|
| 69 |
+
super().__init__()
|
| 70 |
+
self.embedding_dim = embedding_dim
|
| 71 |
+
|
| 72 |
+
def __call__(self, seq_length: int = None, position: torch.tensor = None):
|
| 73 |
+
"""Generates a torch.tensor of sinusoids with different frequencies.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
seq_length: an optional Python int defining the output sequence length.
|
| 77 |
+
if the `position` argument is specified.
|
| 78 |
+
position: [B, seq_length], optional position for each token in the
|
| 79 |
+
sequence, only required when the sequence is packed.
|
| 80 |
+
|
| 81 |
+
Returns:
|
| 82 |
+
[B, seqlen, D] if `position` is specified, else [1, seqlen, D]
|
| 83 |
+
"""
|
| 84 |
+
if position is None:
|
| 85 |
+
assert seq_length is not None
|
| 86 |
+
# [1, seqlen]
|
| 87 |
+
position = torch.arange(seq_length, dtype=torch.float32)[None, :]
|
| 88 |
+
else:
|
| 89 |
+
assert position.ndim == 2, position.shape
|
| 90 |
+
|
| 91 |
+
num_timescales = self.embedding_dim // 2
|
| 92 |
+
log_timescale_increment = torch.log(
|
| 93 |
+
torch.tensor(float(self.max_timescale) / float(self.min_timescale))
|
| 94 |
+
) / torch.maximum(
|
| 95 |
+
torch.tensor(num_timescales, dtype=torch.float32) - 1, torch.tensor(1)
|
| 96 |
+
)
|
| 97 |
+
inv_timescales = self.min_timescale * torch.exp(
|
| 98 |
+
torch.arange(num_timescales, dtype=torch.float32)
|
| 99 |
+
* -log_timescale_increment
|
| 100 |
+
)
|
| 101 |
+
scaled_time = position[:, :, None] * inv_timescales[None, None, :]
|
| 102 |
+
signal = torch.cat((torch.sin(scaled_time), torch.cos(scaled_time)), dim=2)
|
| 103 |
+
# Force usage of `np` rather than `jnp` to compute static values at trace
|
| 104 |
+
# time.
|
| 105 |
+
signal = F.pad(signal, (0, self.embedding_dim % 2, 0, 0, 0, 0))
|
| 106 |
+
return signal
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class MlpBlockWithMask(nn.Module):
|
| 110 |
+
"""Transformer MLP / feed-forward block that supports masking."""
|
| 111 |
+
|
| 112 |
+
def __init__(
|
| 113 |
+
self,
|
| 114 |
+
mlp_dim: int,
|
| 115 |
+
d_model: int,
|
| 116 |
+
use_bias: bool = True,
|
| 117 |
+
dtype: torch.dtype = torch.float32,
|
| 118 |
+
activation_fn: nn.Module = nn.GELU,
|
| 119 |
+
):
|
| 120 |
+
super().__init__()
|
| 121 |
+
|
| 122 |
+
self.mlp_dim = mlp_dim
|
| 123 |
+
self.d_model = d_model
|
| 124 |
+
self.use_bias = use_bias
|
| 125 |
+
self.dtype = dtype
|
| 126 |
+
self.activation_fn = activation_fn
|
| 127 |
+
|
| 128 |
+
self.c_fc = nn.Linear(
|
| 129 |
+
in_features=self.d_model,
|
| 130 |
+
out_features=self.mlp_dim,
|
| 131 |
+
dtype=self.dtype,
|
| 132 |
+
bias=self.use_bias,
|
| 133 |
+
)
|
| 134 |
+
self.c_proj = nn.Linear(
|
| 135 |
+
in_features=self.mlp_dim,
|
| 136 |
+
out_features=self.d_model,
|
| 137 |
+
dtype=self.dtype,
|
| 138 |
+
bias=self.use_bias,
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
def __call__(
|
| 142 |
+
self, inputs: torch.Tensor, mlp_mask: torch.Tensor
|
| 143 |
+
) -> torch.Tensor:
|
| 144 |
+
"""Applies Transformer MlpBlock with mask module."""
|
| 145 |
+
x = self.c_fc(inputs)
|
| 146 |
+
x = self.activation_fn()(x)
|
| 147 |
+
x = x * mlp_mask[..., None] # First masking.
|
| 148 |
+
x = self.c_proj(x)
|
| 149 |
+
x = x * mlp_mask[..., None] # Second masking.
|
| 150 |
+
return x
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
class ResidualAttentionBlock(nn.Module):
|
| 154 |
+
"""Transformer residual attention block."""
|
| 155 |
+
|
| 156 |
+
def __init__(
|
| 157 |
+
self,
|
| 158 |
+
d_model: int,
|
| 159 |
+
n_head: int,
|
| 160 |
+
mlp_dim: int,
|
| 161 |
+
dtype: torch.dtype = torch.float32,
|
| 162 |
+
):
|
| 163 |
+
super().__init__()
|
| 164 |
+
self.d_model = d_model
|
| 165 |
+
self.n_head = n_head
|
| 166 |
+
self.mlp_dim = mlp_dim
|
| 167 |
+
self.dtype = dtype
|
| 168 |
+
|
| 169 |
+
self.attn = nn.MultiheadAttention(d_model, n_head, dtype=self.dtype)
|
| 170 |
+
self.ln_1 = nn.LayerNorm(d_model, dtype=self.dtype)
|
| 171 |
+
self.mlp = MlpBlockWithMask(
|
| 172 |
+
self.mlp_dim,
|
| 173 |
+
d_model,
|
| 174 |
+
use_bias=True,
|
| 175 |
+
dtype=self.dtype,
|
| 176 |
+
activation_fn=nn.ReLU,
|
| 177 |
+
)
|
| 178 |
+
self.ln_2 = nn.LayerNorm(d_model, dtype=self.dtype)
|
| 179 |
+
|
| 180 |
+
def attention(self, x: torch.Tensor, mask: torch.Tensor):
|
| 181 |
+
attn_mask = (
|
| 182 |
+
mask[:, None, None, :]
|
| 183 |
+
.repeat(1, self.n_head, x.shape[0], 1)
|
| 184 |
+
.flatten(0, 1)
|
| 185 |
+
)
|
| 186 |
+
attn_mask[attn_mask == 0] = float('-inf')
|
| 187 |
+
attn_mask[attn_mask == 1] = 0
|
| 188 |
+
return self.attn(x, x, x, need_weights=False, attn_mask=attn_mask)[0]
|
| 189 |
+
|
| 190 |
+
def forward(self, x: torch.Tensor, mask: torch.Tensor):
|
| 191 |
+
x = x + self.attention(self.ln_1(x), mask.permute(1, 0))
|
| 192 |
+
x = x + self.mlp(self.ln_2(x), mask)
|
| 193 |
+
return x, mask
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
class SequentialMultiInput(nn.Sequential):
|
| 197 |
+
"""Sequential module that can take multiple inputs."""
|
| 198 |
+
|
| 199 |
+
def forward(self, *inputs):
|
| 200 |
+
for module in self._modules.values():
|
| 201 |
+
if isinstance(inputs, tuple):
|
| 202 |
+
inputs = module(*inputs)
|
| 203 |
+
else:
|
| 204 |
+
inputs = module(inputs)
|
| 205 |
+
return inputs
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
class Transformer(nn.Module):
|
| 209 |
+
"""Transformer implementation."""
|
| 210 |
+
|
| 211 |
+
def __init__(
|
| 212 |
+
self,
|
| 213 |
+
width: int,
|
| 214 |
+
layers: int,
|
| 215 |
+
heads: int,
|
| 216 |
+
mlp_dim: int,
|
| 217 |
+
dtype: torch.dtype = torch.float32,
|
| 218 |
+
):
|
| 219 |
+
super().__init__()
|
| 220 |
+
self.width = width
|
| 221 |
+
self.layers = layers
|
| 222 |
+
self.heads = heads
|
| 223 |
+
self.mlp_dim = mlp_dim
|
| 224 |
+
self.dtype = dtype
|
| 225 |
+
|
| 226 |
+
self.resblocks = SequentialMultiInput(*[
|
| 227 |
+
ResidualAttentionBlock(self.width, self.heads, self.mlp_dim, self.dtype)
|
| 228 |
+
for _ in range(self.layers)
|
| 229 |
+
])
|
| 230 |
+
|
| 231 |
+
def forward(self, x: torch.Tensor, mask: torch.Tensor):
|
| 232 |
+
return self.resblocks(x, mask)[0]
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class GlobalAvgPooling(nn.Module):
|
| 236 |
+
"""Performs a simple global pooling over the input with optional paddings.
|
| 237 |
+
|
| 238 |
+
Attributes:
|
| 239 |
+
pooling_dims: A list of dims to perform pooling over.
|
| 240 |
+
keepdims: If True, keep dimension of inputs after pooling.
|
| 241 |
+
"""
|
| 242 |
+
|
| 243 |
+
pooling_dims: t.Sequence[int]
|
| 244 |
+
epsilon: float = 1e-8
|
| 245 |
+
|
| 246 |
+
def __init__(
|
| 247 |
+
self, pooling_dims: t.Sequence[int], epsilon: float = 1e-8
|
| 248 |
+
):
|
| 249 |
+
super().__init__()
|
| 250 |
+
self.pooling_dims = pooling_dims
|
| 251 |
+
self.epsilon = epsilon
|
| 252 |
+
|
| 253 |
+
if not all([p_dims >= 0 for p_dims in self.pooling_dims]):
|
| 254 |
+
raise ValueError('pooling_dims must be non-negative integers.')
|
| 255 |
+
|
| 256 |
+
def __call__(
|
| 257 |
+
self,
|
| 258 |
+
inputs: torch.tensor,
|
| 259 |
+
compatible_paddings: torch.tensor,
|
| 260 |
+
):
|
| 261 |
+
"""Applies global average spatial pooling to inputs.
|
| 262 |
+
|
| 263 |
+
Args:
|
| 264 |
+
inputs: An input tensor.
|
| 265 |
+
compatible_paddings: paddings of inputs with shapes compatible with
|
| 266 |
+
inputs, e.g. compatible_paddings with shape [B, 1] for inputs with shape
|
| 267 |
+
[B, D].
|
| 268 |
+
|
| 269 |
+
Returns:
|
| 270 |
+
Output tensor with global pooling applied.
|
| 271 |
+
"""
|
| 272 |
+
padded_value = torch.zeros_like(inputs)
|
| 273 |
+
padded_value = torch.ones_like(inputs) * padded_value
|
| 274 |
+
inputs = torch.where(compatible_paddings > 0, padded_value, inputs)
|
| 275 |
+
valid_inputs = (
|
| 276 |
+
torch.sum(
|
| 277 |
+
1.0 - compatible_paddings,
|
| 278 |
+
self.pooling_dims,
|
| 279 |
+
keepdims=True,
|
| 280 |
+
dtype=inputs.dtype,
|
| 281 |
+
)
|
| 282 |
+
+ self.epsilon
|
| 283 |
+
)
|
| 284 |
+
inputs_sum = torch.sum(inputs, self.pooling_dims, keepdims=True)
|
| 285 |
+
outputs = torch.divide(inputs_sum, valid_inputs).type(inputs.dtype)
|
| 286 |
+
outputs = torch.squeeze(outputs, axis=self.pooling_dims)
|
| 287 |
+
return outputs
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class TextEncoder(nn.Module):
|
| 291 |
+
"""Text encoder implementation."""
|
| 292 |
+
|
| 293 |
+
def __init__(
|
| 294 |
+
self,
|
| 295 |
+
config: t.Dict[str, int],
|
| 296 |
+
vocab_size: int,
|
| 297 |
+
dtype: torch.dtype = torch.float32,
|
| 298 |
+
scale_sqrt_depth: bool = True,
|
| 299 |
+
):
|
| 300 |
+
super().__init__()
|
| 301 |
+
self.vocab_size = vocab_size
|
| 302 |
+
self.dtype = dtype
|
| 303 |
+
self.scale_sqrt_depth = scale_sqrt_depth
|
| 304 |
+
|
| 305 |
+
# The text tower layers are fixed independent of vision tower size.
|
| 306 |
+
self.transformer_layers = config['num_layers']
|
| 307 |
+
self.embedding_dim = config['hidden_size']
|
| 308 |
+
self.transformer_width = config['hidden_size']
|
| 309 |
+
self.mlp_dim = config['mlp_dim']
|
| 310 |
+
self.transformer_heads = config['num_heads']
|
| 311 |
+
|
| 312 |
+
self.token_embedding = nn.Embedding(
|
| 313 |
+
self.vocab_size, self.embedding_dim, dtype=self.dtype
|
| 314 |
+
)
|
| 315 |
+
self.pos_embedder = PositionalEmbedding(embedding_dim=self.embedding_dim)
|
| 316 |
+
self.transformer = Transformer(
|
| 317 |
+
width=self.transformer_width,
|
| 318 |
+
layers=self.transformer_layers,
|
| 319 |
+
heads=self.transformer_heads,
|
| 320 |
+
mlp_dim=self.mlp_dim,
|
| 321 |
+
dtype=self.dtype,
|
| 322 |
+
)
|
| 323 |
+
self.pooling = GlobalAvgPooling(pooling_dims=[1])
|
| 324 |
+
self.ln_final = nn.LayerNorm(self.transformer_width, dtype=self.dtype)
|
| 325 |
+
|
| 326 |
+
def __call__(
|
| 327 |
+
self,
|
| 328 |
+
ids: torch.tensor,
|
| 329 |
+
paddings: torch.tensor,
|
| 330 |
+
):
|
| 331 |
+
"""Applies TextEncoder module."""
|
| 332 |
+
_, seq_length = ids.shape
|
| 333 |
+
mask = (paddings == 0).type(torch.float32)
|
| 334 |
+
mask = mask.permute(1, 0) # NL -> LN
|
| 335 |
+
x = self.token_embedding(ids)
|
| 336 |
+
if self.scale_sqrt_depth:
|
| 337 |
+
x = x * (self.embedding_dim**0.5)
|
| 338 |
+
x = x + self.pos_embedder(seq_length=seq_length).to(x.device)
|
| 339 |
+
x = x.permute(1, 0, 2) # NLD -> LND
|
| 340 |
+
x = self.transformer(x, mask)
|
| 341 |
+
x = x.permute(1, 0, 2) # LND -> NLD
|
| 342 |
+
x = self.ln_final(x)
|
| 343 |
+
x = self.pooling(x, compatible_paddings=paddings[:, :, None])
|
| 344 |
+
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
|