meta-encoder-v1 / README.md
Jinapeng's picture
Upload README.md with huggingface_hub
ed04ebd verified
|
Raw History Blame Contribute Delete
7.93 kB
---
library_name: transformers
pipeline_tag: feature-extraction
tags:
- metaencoder
- muse_glimmer
- multimodal-embedding
- image-text-retrieval
- video-retrieval
- document-retrieval
---
# MetaEncoder v1
![](benchmarks.png)
## Usage
Requires `transformers>=5.15` (the first release with `muse_glimmer`); see `requirements.txt`. One 80 GB GPU holds
the model in bf16.
```python
from modeling_metaencoder import MetaEncoder
model = MetaEncoder.from_pretrained("facebook/meta-encoder-v1")
```
`MetaEncoder` has three calls. Callers pass content and task descriptions; the wrapper writes every prompt itself, in
the layout the encoder was trained on (see [Prompt formatting](#prompt-formatting)).
| Call | Use it to | Returns |
| :--- | :--- | :--- |
| `encode(inputs, instruction=None)` | embed inputs to store, cluster or compare | `(n, 6656)` float32 tensor, L2-normalised |
| `retrieve(query, corpus, top_k=None, query_instruction=None, corpus_instruction=None)` | rank a corpus against a query | `list[Hit]`, best first |
| `decide(state, questions, images=None, video=None)` | answer Jev-style questions about a state | Jev answers with probabilities |
### Inputs
`encode` and `retrieve` take *items*. An item is a string, a PIL image, or a dict with any of `text`, `image`,
`images`, `video` (a list of frames) and `instruction`. The instruction says what the embedding is for, e.g.
"Represent the given image." or "Find a document image that matches the given query."; the same input embeds
differently under different instructions, and queries and documents usually take different ones.
### 1. Encode
```python
from PIL import Image
docs = model.encode([{"image": Image.open("chart.png")}, {"image": Image.open("slide.png")}],
instruction="Understand the content of the provided document image.")
MetaEncoder.similarity(docs, docs) # cosine similarity matrix
```
`inputs` is one item or a list of them; `instruction` applies to the items that carry none of their own.
### 2. Retrieve
```python
hits = model.retrieve("Which quarter had the highest revenue?", docs, top_k=1,
query_instruction="Find a document image that matches the given query.")
hits[0].index, hits[0].score, hits[0].item # position in the corpus, cosine similarity, the corpus item
```
`corpus` is a list of items, or the tensor `encode` returned for them: encode a large corpus once and search the
tensor repeatedly.
### 3. Decide
`decide` takes a Jev decision request, a state and named questions, and answers each question:
```python
result = model.decide(
{"query": "used iphone 15"},
{
"shopping": {"type": "noul", "instructions": "Is the user shopping for a product?"},
"topic": {"type": "choice", "instructions": "Which topic is the query about?",
"criteria": {"electronics": "", "food": "", "travel": "", "other": ""}},
"intent": {"type": "score", "instructions": "How strong is the purchase intent?",
"criteria": ["none", "weak", "moderate", "strong"]},
},
)
result["questions"]["shopping"] # {"value": True, "probabilities": {"false": 0.016, "true": 0.984},
# "confidence": 0.881, "scores": {...}}
result["questions"]["topic"]["value"], result["questions"]["intent"]["value"] # "electronics", 1.89
```
- `state`: what the questions are about. A string, or any JSON value (an object of fields, a list of records).
- `images`, `video`: the state's media: a PIL image or a list of them, and a list of video frames.
- `questions`: `{name: {"type", "instructions", "criteria"}}`, with `instructions` the question itself:
| `type` | `criteria` | `value` | `probabilities` keys |
| :--- | :--- | :--- | :--- |
| `noul` | optional `{"true": ..., "false": ...}` descriptions | `True` / `False` | `"false"`, `"true"` |
| `choice` | `{option: description}`, 2 to 26 options; descriptions may be empty | the chosen option | the options |
| `score` | the ordered list of level labels, e.g. `["none", "weak", "strong"]` | expected level index (float) | `"0"` .. `"n-1"` |
Each answer also has `confidence` (1 minus the normalised entropy of `probabilities`) and `scores` (the raw cosine
similarities). Questions are answered independently; for a question that only applies given another's answer,
decide the first and ignore the second as needed.
`decide_batch(states, questions, images=None, video=None)` answers the same questions for many states (one
`images` / `video` entry per state).
Probabilities are `softmax(score / 0.02)`, the contrastive training temperature. To fit the temperature to your
data, decide a labelled set and call `model.calibrate(answers, labels)` with the per-question answers
(`result["questions"][name]`) and their correct keys (the option, `True`/`False`, or the level index); later
`decide` calls use the fitted temperature.
## Prompt formatting
The wrapper formats every input as in training, so callers never write prompts:
- **System turn** (`chat_template.jinja`): the fixed header ending in "Represent the user's input.", then the item's
instruction after a blank line when it has one. The date line is fixed, so an input embeds the same on any day.
- **User turn**: images and video frames first, then the text. Images keep their native resolution, snapped to a
28-pixel grid, up to 4096 merged tokens. Video is encoded frame by frame; more than 8 frames are subsampled
uniformly to 8, as in training.
- **Pooling**: the last position, the user turn's `<|eot|>`, with left padding. Inputs are never truncated (that
would drop the pooled token); training sequences were at most 16,384 tokens.
`decide` turns each question into a closed-set decision row of training, scored as retrieval over its options:
| Part | Rendered as |
| :--- | :--- |
| query text | the state, a blank line, `Task: {instructions}`, then the options: `Options: a, b, c`, or one `- option` per line when an option is long or punctuated |
| state | a string as is; a JSON value with `json.dumps` |
| query instruction | "Read the task definition and select the option it calls for." for a string state; "Answer the question about the scenario by selecting the correct option." for a JSON state |
| options | `noul`: `No, Yes`; `choice`: the criteria keys; `score`: the level labels |
| candidates | one per option: the option, then its description (if any) on the next line |
| candidate instruction | `Option for the task: {instructions}` when every option is a short label (at most 5 words), else none |
Option descriptions are optional. The yes/no, choice and score rows of training used bare option labels; candidate
descriptions were trained only as records of the entities that options name.
### Without the wrapper
`modeling_metaencoder.py` only formats the prompt and pools; the same embedding with public classes directly:
```python
import torch, torch.nn.functional as F
from safetensors.torch import load_file
from transformers import AutoModel, AutoProcessor
model = AutoModel.from_pretrained(repo, dtype=torch.bfloat16, device_map="auto").eval() # MuseGlimmerModel
processor = AutoProcessor.from_pretrained(repo)
processor.tokenizer.padding_side = "left"
head = load_file(f"{repo}/embedding_head.safetensors")["embedding_head.weight"].to(model.device, torch.bfloat16)
messages = [{"role": "system", "content": "Represent the given image."}, # optional instruction
{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": "a dog"}]}]
text = processor.apply_chat_template(messages, tokenize=False)
inputs = processor(text=[text], images=[image], add_special_tokens=False, max_image_tokens=4096,
return_tensors="pt").to(model.device)
with torch.no_grad():
hidden = model(**inputs).last_hidden_state[:, -1]
embedding = F.normalize((hidden @ head.T).float(), dim=-1)