File size: 4,388 Bytes
c035fa0
 
0c4f83d
 
 
 
 
 
 
 
c035fa0
0c4f83d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
license: apache-2.0
library_name: pytorch
tags:
  - composed-image-retrieval
  - image-retrieval
  - multimodal
  - clip
  - magiclens
pipeline_tag: image-to-image
---

# MagicLens-B (CLIP-B) — PyTorch

PyTorch weights for [MagicLens](https://github.com/google-deepmind/magiclens) (Zhang et al.,
ICML 2024), converted from Google's official JAX/Flax release.

The conversion is **numerically faithful**: on identical inputs this model reproduces the
original's embeddings to cosine similarity **1.00000000** (max absolute difference ~4e-07,
over three seeds).

- **Code:** https://github.com/mat0k/magiclens-pytorch
- **Original:** https://github.com/google-deepmind/magiclens
- **Paper:** https://proceedings.mlr.press/v235/zhang24an.html

## What it does

Composed image retrieval: given a reference image *and* a text instruction ("same dress but
in black"), retrieve the image satisfying both. 166,433,025 parameters, 512-d embeddings.

## Usage

```bash
git clone https://github.com/mat0k/magiclens-pytorch && cd magiclens-pytorch
pip install -e .
```

```python
import torch
from huggingface_hub import hf_hub_download
from PIL import Image
from magiclens import MagicLens, MagicLensImagePreprocess

weights = hf_hub_download("mat0k/magiclens-base-pytorch", "magic_lens_clip_base.pt")

model = MagicLens("base")
model.load_state_dict(torch.load(weights, map_location="cpu"))
model.eval()

preprocess = MagicLensImagePreprocess(224, is_train=False)
images = torch.stack([preprocess(Image.open(p)) for p in ["a.jpg", "b.jpg"]])

with torch.no_grad():
    query = model.encode_mm(images[:1], ["make it blue"])  # reference + instruction
    candidates = model.encode_image(images)                # gallery
    scores = query @ candidates.T                          # both L2-normalised
```

`encode_image` is **not** a plain CLIP image embedding — candidates pass through the same
fusion stack as queries with an empty instruction, matching the official evaluation
protocol. Use `MagicLensImagePreprocess`; the official pipeline scales pixels by each
image's own maximum and squashes aspect ratio rather than cropping, so standard CLIP
preprocessing gives different results.

## Verification

**Parity with the original JAX model** — identical inputs through both implementations:

```
min cosine 1.00000000   max|diff| 4.263e-07
```

Preprocessing parity on real photos at native resolution: 7.8e-06.

**Benchmarks**, zero-shot, against the paper's MagicLens-B / CLIP-B row:

| Benchmark | Metric | Paper | This port |
|---|---|---|---|
| Fashion-IQ (val) | overall R@10 | 26.3 | 25.90 |
| Fashion-IQ (val) | overall R@50 | 47.4 | 48.41 |
| CIRR (test) | R@1 | 27.0 | 29.52 |
| CIRR (test) | R@5 | 58.0 | 59.61 |
| CIRR (test) | R@10 | 70.9 | 72.63 |
| CIRR (test) | R@50 | 91.1 | 91.74 |
| CIRR (test) | R_subset@1 | 66.7 | 67.35 |

CIRR was scored by the [official evaluation server](https://cirr.cecs.anu.edu.au/), not by
us. Mean absolute difference 0.77 (Fashion-IQ) and 1.11 (CIRR). Since the model is provably
identical, these residuals come from evaluation-pipeline differences, not the weights — the
released code contains no CIRR evaluation at all, so the paper's CIRR numbers came from an
internal pipeline that cannot be matched exactly.

## Fine-tuning

Unlike the official release, this port supports training — the model returns an InfoNCE
loss, so an ordinary PyTorch loop works. See `examples/finetune_demo.py` in the GitHub repo.
Note the batch size *is* the negative pool: MagicLens was pretrained at batch 2048, and
fine-tuning at batch 32 measurably degraded the model in our experiments.

## License and citation

Code Apache 2.0. These weights are derived from Google DeepMind's release and remain under
its CC-BY 4.0 terms for model materials.

```bibtex
@inproceedings{zhang2024magiclens,
  title     = {{M}agic{L}ens: Self-Supervised Image Retrieval with Open-Ended Instructions},
  author    = {Zhang, Kai and Luan, Yi and Hu, Hexiang and Lee, Kenton and Qiao, Siyuan
               and Chen, Wenhu and Su, Yu and Chang, Ming-Wei},
  booktitle = {Proceedings of the 41st International Conference on Machine Learning},
  pages     = {59403--59420},
  year      = {2024},
  volume    = {235},
  series    = {Proceedings of Machine Learning Research},
  publisher = {PMLR},
  url       = {https://proceedings.mlr.press/v235/zhang24an.html}
}
```