File size: 8,004 Bytes
588581a
 
 
d9d575a
588581a
d9d575a
 
 
588581a
 
 
0e9cd89
588581a
 
 
 
d9d575a
588581a
c231ef5
 
 
588581a
c231ef5
b3cc655
c231ef5
b3cc655
588581a
b374ae7
 
8b47e77
b374ae7
c231ef5
 
 
 
 
 
 
 
b3cc655
b374ae7
 
 
 
 
 
 
 
fa1bf1c
 
 
c231ef5
 
 
 
588581a
0e9cd89
 
 
 
588581a
c231ef5
 
 
 
 
 
588581a
d9d575a
c231ef5
588581a
b3cc655
 
c231ef5
 
0e9cd89
588581a
 
74f228a
c231ef5
74f228a
0e9cd89
 
c231ef5
 
0e9cd89
 
c231ef5
0e9cd89
c231ef5
 
 
 
0e9cd89
 
c231ef5
 
 
 
 
 
 
 
 
74f228a
 
c231ef5
 
74f228a
c231ef5
 
 
 
588581a
d9d575a
 
0e9cd89
d9d575a
b3cc655
c231ef5
 
d9d575a
 
c231ef5
 
d9d575a
 
0e9cd89
d9d575a
c231ef5
 
 
d9d575a
c231ef5
 
588581a
c231ef5
588581a
c231ef5
 
 
588581a
c231ef5
0e9cd89
c231ef5
0e9cd89
 
c231ef5
6d58742
c231ef5
6d58742
b374ae7
 
 
 
 
 
6d58742
c231ef5
588581a
c231ef5
 
 
588581a
c231ef5
0e9cd89
 
 
 
4380add
c231ef5
 
 
 
 
 
 
 
 
 
588581a
 
 
c231ef5
 
 
588581a
c231ef5
588581a
c231ef5
 
 
 
 
 
 
 
 
 
 
 
d9d575a
c231ef5
 
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
---
license: mit
language: en
library_name: fastembed
tags:
  - fastembed
  - qdrant
  - onnx
  - retrieval
  - asymmetric-dual-encoder
  - edge
  - research-preview
base_model: NovaSearch/stella_en_400M_v5
pipeline_tag: feature-extraction
---

# constella-zero

constella-zero is a small English query encoder for semantic search. It uses an int8 token lookup
table instead of a transformer, making it useful when query latency matters more than maximum
retrieval quality.

It produces normalized 1024-dimensional vectors that search documents encoded by
[`stella-en-400M-v5-doc-onnx`](https://huggingface.co/Qdrant/stella-en-400M-v5-doc-onnx).
The same document index also works with the stronger
[`constella-nano`](https://huggingface.co/Qdrant/constella-nano) query encoder.

> Research preview: Native FastEmbed support currently requires the Constella preview branch
> shown below. The published evaluation is limited to the results described in this card.
> Full BEIR benchmarking is underway. Results from that broader evaluation are not included yet.

| Property | Value |
|---|---|
| Role | Query encoder |
| Output | 1024-dimensional normalized fp32 vector |
| Architecture | 30,522 x 1,024 int8 token lookup table |
| Languages | English |
| Maximum input length | 512 tokens |
| Query prefix | None |
| Document encoder | `Qdrant/stella-en-400M-v5-doc-onnx` |
| Recommended retrieval | Hybrid with BM25 and DBSF at prefetch 100 |

## The Constella family

The name Constella combines "constellation" and "Stella." The document embeddings are the fixed
stars, and the query encoder navigates their shared vector space.

Zero and Nano are swappable at query time. Both can search the same document index, so you can
choose between them without re-encoding documents or rebuilding the collection. Their rankings
differ: Zero is the faster option, while Nano has higher retrieval scores on the six reported
datasets. The "zero" name refers to its transformer-free query path.

## Installation

Native FastEmbed support is currently available from the Constella preview branch:

```console
pip install "fastembed @ git+https://github.com/Dylancouzon/fastembed.git@constella-research-preview"
pip install qdrant-client
```

## Usage

Encode documents once with the document model, then encode queries with constella-zero. The
example below creates an in-memory Qdrant collection, but the vectors can be used with any vector
database that supports cosine similarity.

```python
from fastembed import TextEmbedding
from qdrant_client import QdrantClient, models

NAME = "Qdrant/constella-zero"
DOC_NAME = "Qdrant/stella-en-400M-v5-doc-onnx"

documents = [
    "mRNA vaccines deliver messenger RNA encoding a viral antigen.",
    "The Treaty of Westphalia ended the Thirty Years' War in 1648.",
]

document_model = TextEmbedding(DOC_NAME)

client = QdrantClient(":memory:")
client.create_collection(
    "documents",
    vectors_config=models.VectorParams(size=1024, distance=models.Distance.COSINE),
)
client.upsert(
    "documents",
    points=[
        models.PointStruct(id=i, vector=embedding.tolist(), payload={"text": text})
        for i, (text, embedding) in enumerate(
            zip(documents, document_model.embed(documents))
        )
    ],
)

query_model = TextEmbedding(NAME)
query_embedding = next(iter(query_model.embed(["how do mRNA vaccines work?"])))
results = client.query_points(
    "documents", query=query_embedding.tolist(), limit=2
).points

for result in results:
    print(result.score, result.payload["text"])
```

FastEmbed handles pooling and L2 normalization. Do not use the document model as an unprompted
query encoder. Use constella-zero, constella-nano, or Stella's prompted query path instead.

### NumPy reference implementation

The repository also includes `zero_encoder.py`, a reference implementation that does not require
FastEmbed or ONNX Runtime:

```python
from huggingface_hub import snapshot_download
import sys

model_directory = snapshot_download("Qdrant/constella-zero")
sys.path.insert(0, model_directory)

from zero_encoder import ZeroQueryEncoder

model = ZeroQueryEncoder(model_directory, variant="int8")
query_embeddings = model.encode(["how do mRNA vaccines work?"])
```

## How it works

The encoder tokenizes each query with WordPiece, looks up a learned vector for every token, and
combines those vectors into one query embedding. Repeated tokens receive diminishing weight: a
token that occurs `c` times contributes a total weight of `sqrt(c)`. The result is L2-normalized.

This is a bag-of-tokens model. It does not represent word order, syntax, or negation directly.
Learned token weights are already included in the table.

## Retrieval results

The table reports exact-search nDCG@10. ArguAna and FiQA are marked because the Stella teacher
discloses training or evaluation contact with those datasets. Results on those two datasets should
therefore be interpreted separately from the other four.

| System | NFCorpus | SCIDOCS | SciFact | TREC-COVID | ArguAna* | FiQA* |
|---|---:|---:|---:|---:|---:|---:|
| constella-zero | 0.3124 | 0.1677 | 0.6101 | 0.5490 | 0.5916 | 0.3728 |
| constella-nano | 0.363080 | 0.217710 | 0.721097 | 0.787116 | 0.623296 | 0.477765 |
| BM25 | 0.3180 | 0.1565 | 0.6791 | 0.6099 | 0.4878 | 0.2532 |
| Stella query encoder | 0.4134 | 0.2395 | 0.7796 | 0.8234 | 0.6369 | 0.5536 |

Note: Stella discloses training or evaluation contact with ArguAna and FiQA.

The recommended deployment setup for Zero is hybrid retrieval. Retrieve with both Zero and BM25,
then combine their results with Qdrant's distribution-based score fusion (DBSF), prefetching 100
candidates from each side. This setup scored 0.4887 mean nDCG@10 across all six datasets and
0.4912 across the four datasets without disclosed Stella contact. The evaluated lexical side used
`bm25s` with Lucene defaults, so results may differ with another BM25 implementation. Dense-only
retrieval remains supported when a lexical index is unavailable or unnecessary.

## Query encoding cost

These measurements cover the query encoder only. They use batch size 1, four CPU threads, five
warmups, and twenty synthetic 20-word queries in each of three fresh processes. They do not
include vector search or end-to-end application latency.

| Model | Load time | First query | Warm query p50 | Peak RSS | Measured assets |
|---|---:|---:|---:|---:|---:|
| constella-zero | 0.2618 s | 0.3529 ms | 0.1119 ms | 275.4 MiB | 90.1 MiB |
| bge-small | 0.6726 s | 8.2401 ms | 6.8400 ms | 291.0 MiB | 127.6 MiB |
| constella-nano | 0.6907 s | 7.6685 ms | 7.2511 ms | 280.9 MiB | 132.3 MiB |

## Files

| File | Purpose | Size |
|---|---|---:|
| `model.onnx` | Pooled and normalized FastEmbed graph | 31 MB |
| `model_tokens.onnx` | Token-level output for custom pooling | 31 MB |
| `model.npz` | NumPy reference implementation | 94 MB |

Both ONNX graphs use opset 17 and standard operators. The int8 table is dequantized inside the
graph with one fp32 scale per row.

## Training

The released table was trained by L2 regression against Stella query embeddings. Training used
338,076 usable query-document pairs plus 220,632 query-only rows from Amazon ESCI, FEVER,
HotpotQA, SQuAD, NQ Open, TriviaQA, and Mr. TyDi English. MS MARCO was excluded.

Wikipedia-derived data retains CC BY-SA attribution. Amazon ESCI and TriviaQA are Apache-2.0.

## Limitations

- The model is English-only and truncates inputs after 512 tokens.
- As a bag-of-tokens model, it is weak at distinctions that depend on word order, syntax, or
  negation.
- Document indexing still requires the 400M-parameter Stella document encoder.

## License and provenance

The model is MIT licensed. It was distilled from
[`NovaSearch/stella_en_400M_v5`](https://huggingface.co/NovaSearch/stella_en_400M_v5) at revision
`ffeb2b7ee715c226d4ffe5e4619f7dbb48624c20`, which is also MIT licensed.

The released int8 table has SHA-256
`a7007b1a6af120b976f093fd69ddcb5001996ec0b84b5864b4fd25d7af878abf`.