File size: 6,890 Bytes
27709a6
7305900
28ba2c0
 
 
 
 
 
 
27709a6
28ba2c0
 
 
 
 
 
 
 
de3bd80
28ba2c0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de3bd80
28ba2c0
 
 
de3bd80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
---
license: mit
library_name: transformers
pipeline_tag: image-feature-extraction
tags:
- mage-vl
- vision-encoder
- codec-vit
- video-understanding
---

<h1 align="center">Mage-ViT<br><span style="font-size: 0.55em; font-weight: normal;">A Codec-Native Visual Encoder Trained from Scratch</span></h1>

<p align="center">
  <a href="https://microsoft.github.io/Mage"><img alt="Project Page" src="https://img.shields.io/badge/%F0%9F%8C%90-Project%20Page-blue" height="22" /></a>
  <a href="https://github.com/microsoft/Mage"><img src="https://img.shields.io/badge/Code-GitHub-181717?logo=github" alt="GitHub" height="22"></a>
  <a href="https://huggingface.co/microsoft/Mage-VL"><img alt="Mage-VL" src="https://img.shields.io/badge/%F0%9F%A4%97-Mage--VL-yellow" height="22" /></a>
  <a href="https://huggingface.co/microsoft/Mage-ViT"><img alt="Mage-ViT" src="https://img.shields.io/badge/%F0%9F%A4%97-Mage--ViT-yellow" height="22" /></a>
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-green" alt="License: MIT" height="22"></a>
</p>

---

**Mage-ViT** is the visual encoder at the core of **[Mage-VL](https://huggingface.co/microsoft/Mage-VL)**. It is a *Codec-ViT* built primarily for video, where a single image is simply the degenerate one-frame case. Mage-ViT follows a **codec-aligned sparsity** principle: visual tokens should be spent where a video codec spends bits, since codec bit-allocation is a natural proxy for spatio-temporal importance. On a shared `16Γ—16` patch grid it keeps every anchor (I-frame) patch and only the motion-salient predicted (P-frame) patches, while a shared **3D rotary position encoding** preserves spatio-temporal structure even after large fractions of the grid are dropped.

This repository is the **ViT-pre-trained checkpoint only** β€” it has not gone through the joint VLM training with the language model. Use it as a data-efficient, codec-native visual encoder, or as a drop-in ViT for your own multimodal training.

## ✨ Highlights

- **Codec-driven patchifier.** Patches are selected by a per-patch importance map derived from the codec β€” motion vectors + P-frame residual energy for HEVC/H.265, or the learned rate map of the neural codec DCVC-RT. For a 64-frame clip it keeps all I-frame patches plus the top-*k* P-frame patches within a **4096-token budget (~75% token reduction)**. Chunk-wise and collage patchification are also supported.
- **Codec-agnostic.** The same interface accepts a traditional codec (HEVC/H.265) or a neural codec (DCVC-RT) with no architecture or retraining change.
- **Trained from scratch.** No billion-scale image-text ViT initialization β€” Mage-ViT is optimized with a large-scale **cluster-discrimination** objective on **~100M unlabeled images/videos**.

## πŸ—οΈ Architecture

A **24-layer pre-norm Vision Transformer** trunk (hidden size `1024`, `16` attention heads, GELU MLP at 4Γ— expansion) processes the variable-length token sequence produced by the codec-driven patchifier, followed by a multi-head attention pooling head. The standalone encoder in this repo consumes `pixel_values` (and optional `patch_positions`); codec-driven patch **selection** is applied upstream in the Mage-VL data pipeline.

Pre-training is a two-stage, from-scratch recipe in bf16: **(1)** variable-resolution image pre-training (224–448), then **(2)** joint image + video pre-training (video at resolution 256, 64 frames per clip, 4096-token budget).

## πŸ“¦ Installation

Mage-ViT follows the Mage-VL runtime:

```bash
pip install "transformers>=5.7" torch torchvision pillow
# optional, for the fastest GPU attention path:
# pip install flash-attn --no-build-isolation
```

Attention is dispatched internally to `sdpa` (default) β†’ `flash_attention_2` β†’ `eager`, so **flash-attn is optional** and the model runs on CPU or GPU out of the box.

## πŸš€ Encoding an image

```python
import torch
from PIL import Image
from transformers import AutoModel, AutoImageProcessor

model = AutoModel.from_pretrained(
    "microsoft/Mage-ViT", trust_remote_code=True
).to("cuda").eval()
processor = AutoImageProcessor.from_pretrained(
    "microsoft/Mage-ViT", trust_remote_code=True
)

image = Image.open("your_image.jpg")
pixel_values = processor(images=image, return_tensors="pt")["pixel_values"].to("cuda")

with torch.no_grad():
    out = model(pixel_values)              # pixel_values: [B, 3, H, W]

patch_features = out.last_hidden_state     # [B, num_patches, 1024]
pooled_feature = out.pooler_output         # [B, 1024]
```

Pass `attn_implementation="flash_attention_2"` (or `"sdpa"` / `"eager"`) to `from_pretrained` to pick the attention backend.

## 🎞️ Encoding a video

Stack the preprocessed frames into `[B, C, T, H, W]` and pass a `patch_positions` tensor of shape `[B, T * tokens_per_frame, 3]` giving the `(t, h, w)` grid coordinate of every patch:

```python
import torch
from PIL import Image

PATCH = 16

def build_patch_positions(num_frames, target_frames, grid_h, grid_w, device="cuda"):
    # temporal index for each frame, spread across the target timeline
    t = torch.linspace(0, target_frames - 1, num_frames, device=device).long()
    t = t.repeat_interleave(grid_h * grid_w)                    # [T * H * W]
    h = torch.arange(grid_h, device=device).repeat_interleave(grid_w).repeat(num_frames)
    w = torch.arange(grid_w, device=device).repeat(grid_h).repeat(num_frames)
    return torch.stack([t, h, w], dim=-1).unsqueeze(0)          # [1, T*H*W, 3]

frames = [Image.open(f"frame_{i}.jpg") for i in range(16)]      # your sampled frames
pv = processor(images=frames, return_tensors="pt")["pixel_values"]   # [T, C, H, W]
video = pv.unsqueeze(0).permute(0, 2, 1, 3, 4).to("cuda")            # [1, C, T, H, W]

gh, gw = video.shape[-2] // PATCH, video.shape[-1] // PATCH
patch_positions = build_patch_positions(num_frames=16, target_frames=64,
                                        grid_h=gh, grid_w=gw)

with torch.no_grad():
    out = model(video, patch_positions=patch_positions)
```

## πŸ“€ Outputs

| Field | Shape | Description |
| ----- | ----- | ----------- |
| `last_hidden_state` | `[B, num_patches, 1024]` | per-patch features after the final layer norm |
| `pooler_output`     | `[B, 1024]`              | global feature from the multi-head attention pooling head |

## πŸ“‹ Specifications

| | |
| --- | --- |
| Architecture | Codec-ViT (pre-norm ViT, SigLIP-style MLP) |
| Parameters | ~316M |
| Hidden size / MLP | 1024 / 4096 |
| Layers / heads | 24 / 16 |
| Patch size | 16 |
| Position encoding | shared 3D rotary (4:6:6 split over T:H:W) |
| Pooling | learned-probe multi-head attention head |
| Pre-training resolution | images 224–448 (variable), video 256 |
| Weights dtype | bfloat16 |
| License | MIT |

## πŸ“„ License

Released under the [MIT License](https://opensource.org/licenses/MIT).