zuhut-align 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zuhut_align-0.1.0/PKG-INFO +264 -0
- zuhut_align-0.1.0/README.md +207 -0
- zuhut_align-0.1.0/setup.cfg +4 -0
- zuhut_align-0.1.0/setup.py +83 -0
- zuhut_align-0.1.0/zuhut_align/__init__.py +52 -0
- zuhut_align-0.1.0/zuhut_align/adapters/__init__.py +3 -0
- zuhut_align-0.1.0/zuhut_align/adapters/zero_shot.py +129 -0
- zuhut_align-0.1.0/zuhut_align/client.py +202 -0
- zuhut_align-0.1.0/zuhut_align/engine/__init__.py +73 -0
- zuhut_align-0.1.0/zuhut_align/mlp/__init__.py +3 -0
- zuhut_align-0.1.0/zuhut_align/mlp/phase_mlp.py +330 -0
- zuhut_align-0.1.0/zuhut_align/utils/__init__.py +3 -0
- zuhut_align-0.1.0/zuhut_align/utils/normalize.py +8 -0
- zuhut_align-0.1.0/zuhut_align.egg-info/PKG-INFO +264 -0
- zuhut_align-0.1.0/zuhut_align.egg-info/SOURCES.txt +16 -0
- zuhut_align-0.1.0/zuhut_align.egg-info/dependency_links.txt +1 -0
- zuhut_align-0.1.0/zuhut_align.egg-info/requires.txt +23 -0
- zuhut_align-0.1.0/zuhut_align.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zuhut-align
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cross-model embedding alignment: bridge any two embedding models, zero-shot.
|
|
5
|
+
Home-page: https://github.com/zuhut-ai/zuhut-align
|
|
6
|
+
Author: ZUHUT
|
|
7
|
+
Author-email: contact@zuhut.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.zuhut.ai
|
|
9
|
+
Project-URL: Homepage, https://zuhut.ai
|
|
10
|
+
Project-URL: Bug Reports, https://github.com/zuhut-ai/zuhut-align/issues
|
|
11
|
+
Project-URL: Source, https://github.com/zuhut-ai/zuhut-align
|
|
12
|
+
Keywords: embeddings,alignment,cross-model,retrieval,vector search,semantic search,sentence transformers,zero-shot,contrastive learning
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
Requires-Dist: numpy>=1.24
|
|
27
|
+
Requires-Dist: scipy>=1.10
|
|
28
|
+
Provides-Extra: mlp
|
|
29
|
+
Requires-Dist: torch>=2.0; extra == "mlp"
|
|
30
|
+
Requires-Dist: sentence-transformers>=2.7; extra == "mlp"
|
|
31
|
+
Provides-Extra: qdrant
|
|
32
|
+
Requires-Dist: torch>=2.0; extra == "qdrant"
|
|
33
|
+
Requires-Dist: sentence-transformers>=2.7; extra == "qdrant"
|
|
34
|
+
Requires-Dist: qdrant-client>=1.9; extra == "qdrant"
|
|
35
|
+
Provides-Extra: full
|
|
36
|
+
Requires-Dist: torch>=2.0; extra == "full"
|
|
37
|
+
Requires-Dist: sentence-transformers>=2.7; extra == "full"
|
|
38
|
+
Requires-Dist: qdrant-client>=1.9; extra == "full"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
42
|
+
Requires-Dist: black; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff; extra == "dev"
|
|
44
|
+
Requires-Dist: mypy; extra == "dev"
|
|
45
|
+
Dynamic: author
|
|
46
|
+
Dynamic: author-email
|
|
47
|
+
Dynamic: classifier
|
|
48
|
+
Dynamic: description
|
|
49
|
+
Dynamic: description-content-type
|
|
50
|
+
Dynamic: home-page
|
|
51
|
+
Dynamic: keywords
|
|
52
|
+
Dynamic: project-url
|
|
53
|
+
Dynamic: provides-extra
|
|
54
|
+
Dynamic: requires-dist
|
|
55
|
+
Dynamic: requires-python
|
|
56
|
+
Dynamic: summary
|
|
57
|
+
|
|
58
|
+
# zuhut-align
|
|
59
|
+
|
|
60
|
+
> **Cross-model embedding alignment.** Bridge any two embedding models — zero-shot, no labels, no reindexing.
|
|
61
|
+
|
|
62
|
+
[](https://pypi.org/project/zuhut-align)
|
|
63
|
+
[](LICENSE)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## The Problem
|
|
68
|
+
|
|
69
|
+
You have **millions of documents** indexed with `bge-large-en-v1.5` (1024d).
|
|
70
|
+
You want to query with `MiniLM` (384d) — faster, cheaper, fits on edge.
|
|
71
|
+
|
|
72
|
+
Without alignment: **R@5 = 0.13** (unusable).
|
|
73
|
+
With ZUHUT: **R@5 = 0.70** (91% of same-model ceiling) — zero reindexing.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Level 1 — ZeroShot (free, no GPU needed)
|
|
81
|
+
pip install zuhut-align
|
|
82
|
+
|
|
83
|
+
# Level 2 — PhaseMLP training (requires PyTorch)
|
|
84
|
+
pip install 'zuhut-align[mlp]'
|
|
85
|
+
|
|
86
|
+
# Full install
|
|
87
|
+
pip install 'zuhut-align[full]'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
### Level 1 — ZeroShot (Free)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from zuhut_align import ZUHUTClient
|
|
98
|
+
|
|
99
|
+
# No API key needed
|
|
100
|
+
client = ZUHUTClient()
|
|
101
|
+
adapter = client.create_adapter(src_dim=1024) # bge-large → MiniLM space
|
|
102
|
+
|
|
103
|
+
# Align your corpus vectors once (store the result in Qdrant/FAISS/etc.)
|
|
104
|
+
corpus_aligned = adapter.transform(corpus_vectors_1024d) # shape (N, 384)
|
|
105
|
+
|
|
106
|
+
# Align query at search time
|
|
107
|
+
query_aligned = adapter.transform(query_vector_1024d) # shape (1, 384)
|
|
108
|
+
|
|
109
|
+
# Now search corpus_aligned with query_aligned using cosine similarity
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Result:** R@5 jumps from 0.13 → 0.40. Zero training. Zero data. Instant.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### Level 2 — PhaseMLP (Paid, ~5 min training)
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from zuhut_align import ZUHUTClient
|
|
120
|
+
from sentence_transformers import SentenceTransformer
|
|
121
|
+
|
|
122
|
+
model_b = SentenceTransformer("paraphrase-MiniLM-L6-v2")
|
|
123
|
+
|
|
124
|
+
client = ZUHUTClient(api_key="zuhut-your-key-here")
|
|
125
|
+
trainer = client.create_adapter(src_dim=1024, tier="phasemlp")
|
|
126
|
+
|
|
127
|
+
# Train on 200–500 sample docs from your corpus (NOT the full corpus)
|
|
128
|
+
# Training time: ~5 min on CPU regardless of corpus size
|
|
129
|
+
trainer.train(
|
|
130
|
+
corpus_texts=sample_docs, # List[str], 200–500 docs
|
|
131
|
+
embedder=model_b.encode, # Your query-side encoder
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Now trainer is a PhaseMLP_Trainer with a trained .mlp
|
|
135
|
+
adapter = trainer # .transform() is available after training
|
|
136
|
+
|
|
137
|
+
corpus_aligned = adapter.transform(corpus_vectors_1024d)
|
|
138
|
+
query_aligned = adapter.transform(query_vector_1024d)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Result:** R@5 = **0.70** — 91% of same-model ceiling.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Full Qdrant Example
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from zuhut_align import ZUHUTClient
|
|
149
|
+
from sentence_transformers import SentenceTransformer
|
|
150
|
+
from qdrant_client import QdrantClient
|
|
151
|
+
from qdrant_client.models import Distance, VectorParams, PointStruct
|
|
152
|
+
|
|
153
|
+
# 1. Models
|
|
154
|
+
model_a = SentenceTransformer("BAAI/bge-large-en-v1.5") # corpus model
|
|
155
|
+
model_b = SentenceTransformer("paraphrase-MiniLM-L6-v2") # query model
|
|
156
|
+
|
|
157
|
+
# 2. ZUHUT alignment (zero-shot)
|
|
158
|
+
client = ZUHUTClient()
|
|
159
|
+
adapter = client.create_adapter(src_dim=1024)
|
|
160
|
+
|
|
161
|
+
# 3. Encode & align corpus
|
|
162
|
+
corpus_vecs = model_a.encode(corpus_docs, normalize_embeddings=True)
|
|
163
|
+
corpus_zuhut = adapter.transform(corpus_vecs) # shape (N, 384)
|
|
164
|
+
|
|
165
|
+
# 4. Index into Qdrant (384d, one-time)
|
|
166
|
+
qdrant = QdrantClient(host="localhost", port=6333)
|
|
167
|
+
qdrant.recreate_collection("my_corpus", vectors_config=VectorParams(
|
|
168
|
+
size=384, distance=Distance.COSINE
|
|
169
|
+
))
|
|
170
|
+
qdrant.upsert("my_corpus", points=[
|
|
171
|
+
PointStruct(id=i, vector=corpus_zuhut[i].tolist())
|
|
172
|
+
for i in range(len(corpus_docs))
|
|
173
|
+
])
|
|
174
|
+
|
|
175
|
+
# 5. Search with cheap query model
|
|
176
|
+
query = "how do neural networks learn?"
|
|
177
|
+
q_vec = model_b.encode(query, normalize_embeddings=True)
|
|
178
|
+
q_zuhut = adapter.transform(q_vec)
|
|
179
|
+
|
|
180
|
+
results = qdrant.query_points("my_corpus", query=q_zuhut[0].tolist(), limit=5)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## How It Works
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
Source Model (1024d)
|
|
189
|
+
↓
|
|
190
|
+
SVD Projection → 384d
|
|
191
|
+
↓
|
|
192
|
+
Procrustes Rotation ← synthetic anchors (N=250, SEED=42)
|
|
193
|
+
↓
|
|
194
|
+
L2 Normalize
|
|
195
|
+
↓
|
|
196
|
+
[PhaseMLP] ← optional, trained on your corpus pseudo-pairs
|
|
197
|
+
↓
|
|
198
|
+
Target Space (384d) — compatible with your indexed corpus
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Zero-Shot (Level 1)
|
|
202
|
+
- Generates 250 synthetic Gaussian anchor pairs
|
|
203
|
+
- Learns an SVD projection (dim reduction) + Procrustes rotation
|
|
204
|
+
- No real data, no training, reproducible across runs
|
|
205
|
+
|
|
206
|
+
### PhaseMLP (Level 2)
|
|
207
|
+
- Residual MLP (x + net(x)) with zero-init last layer
|
|
208
|
+
- Trained with InfoNCE contrastive loss on corpus pseudo-pairs
|
|
209
|
+
- Pseudo-pairs via word dropout (10% random word removal)
|
|
210
|
+
- Training invariant to corpus size: 200–500 docs → 5 min on CPU
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Benchmark Results
|
|
215
|
+
|
|
216
|
+
Tested on Qdrant with BGE-large (corpus) vs MiniLM (query):
|
|
217
|
+
|
|
218
|
+
| Condition | R@5 | vs Ceiling |
|
|
219
|
+
|---|---|---|
|
|
220
|
+
| Same-model baseline (ceiling) | 0.767 | — |
|
|
221
|
+
| Cross-model, no alignment | 0.133 | -63.3% |
|
|
222
|
+
| **+ ZUHUT ZeroShot (Level 1)** | **0.400** | **-36.7%** |
|
|
223
|
+
| **+ ZUHUT PhaseMLP v3 (Level 2)** | **0.700** | **-6.7%** |
|
|
224
|
+
|
|
225
|
+
Vector databases tested: Qdrant ✓, FAISS ✓, LanceDB ✓, ChromaDB ✓
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Tiers
|
|
230
|
+
|
|
231
|
+
| | Level 1 ZeroShot | Level 2 PhaseMLP | Level 3 Engine |
|
|
232
|
+
|---|---|---|---|
|
|
233
|
+
| **Price** | Free | Paid | Enterprise |
|
|
234
|
+
| **Training data** | None | 200–500 docs | None |
|
|
235
|
+
| **Training time** | 0s | ~5 min CPU | 0s |
|
|
236
|
+
| **R@5 gain** | +30% | +57% | +57% |
|
|
237
|
+
| **Inference speed** | Fast (NumPy) | Fast (PyTorch) | Ultra (Rust) |
|
|
238
|
+
| **API key** | Not required | Required | License key |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Requirements
|
|
243
|
+
|
|
244
|
+
- Python ≥ 3.9
|
|
245
|
+
- numpy ≥ 1.24
|
|
246
|
+
- scipy ≥ 1.10
|
|
247
|
+
- torch ≥ 2.0 *(Level 2 only)*
|
|
248
|
+
- sentence-transformers ≥ 2.7 *(Level 2 only)*
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
Apache 2.0 — Python SDK is open source.
|
|
255
|
+
The Rust engine (`zuhri_engine`) is proprietary (Level 3 / Enterprise).
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Links
|
|
260
|
+
|
|
261
|
+
- 🌐 Website: [zuhut.ai](https://zuhut.ai) *(coming soon)*
|
|
262
|
+
- 📖 Docs: [docs.zuhut.ai](https://docs.zuhut.ai) *(coming soon)*
|
|
263
|
+
- 📧 Enterprise: contact@zuhut.ai
|
|
264
|
+
- 🐛 Issues: [github.com/zuhut-ai/zuhut-align/issues](https://github.com/zuhut-ai/zuhut-align/issues)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# zuhut-align
|
|
2
|
+
|
|
3
|
+
> **Cross-model embedding alignment.** Bridge any two embedding models — zero-shot, no labels, no reindexing.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/zuhut-align)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## The Problem
|
|
11
|
+
|
|
12
|
+
You have **millions of documents** indexed with `bge-large-en-v1.5` (1024d).
|
|
13
|
+
You want to query with `MiniLM` (384d) — faster, cheaper, fits on edge.
|
|
14
|
+
|
|
15
|
+
Without alignment: **R@5 = 0.13** (unusable).
|
|
16
|
+
With ZUHUT: **R@5 = 0.70** (91% of same-model ceiling) — zero reindexing.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Level 1 — ZeroShot (free, no GPU needed)
|
|
24
|
+
pip install zuhut-align
|
|
25
|
+
|
|
26
|
+
# Level 2 — PhaseMLP training (requires PyTorch)
|
|
27
|
+
pip install 'zuhut-align[mlp]'
|
|
28
|
+
|
|
29
|
+
# Full install
|
|
30
|
+
pip install 'zuhut-align[full]'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Level 1 — ZeroShot (Free)
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from zuhut_align import ZUHUTClient
|
|
41
|
+
|
|
42
|
+
# No API key needed
|
|
43
|
+
client = ZUHUTClient()
|
|
44
|
+
adapter = client.create_adapter(src_dim=1024) # bge-large → MiniLM space
|
|
45
|
+
|
|
46
|
+
# Align your corpus vectors once (store the result in Qdrant/FAISS/etc.)
|
|
47
|
+
corpus_aligned = adapter.transform(corpus_vectors_1024d) # shape (N, 384)
|
|
48
|
+
|
|
49
|
+
# Align query at search time
|
|
50
|
+
query_aligned = adapter.transform(query_vector_1024d) # shape (1, 384)
|
|
51
|
+
|
|
52
|
+
# Now search corpus_aligned with query_aligned using cosine similarity
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Result:** R@5 jumps from 0.13 → 0.40. Zero training. Zero data. Instant.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Level 2 — PhaseMLP (Paid, ~5 min training)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from zuhut_align import ZUHUTClient
|
|
63
|
+
from sentence_transformers import SentenceTransformer
|
|
64
|
+
|
|
65
|
+
model_b = SentenceTransformer("paraphrase-MiniLM-L6-v2")
|
|
66
|
+
|
|
67
|
+
client = ZUHUTClient(api_key="zuhut-your-key-here")
|
|
68
|
+
trainer = client.create_adapter(src_dim=1024, tier="phasemlp")
|
|
69
|
+
|
|
70
|
+
# Train on 200–500 sample docs from your corpus (NOT the full corpus)
|
|
71
|
+
# Training time: ~5 min on CPU regardless of corpus size
|
|
72
|
+
trainer.train(
|
|
73
|
+
corpus_texts=sample_docs, # List[str], 200–500 docs
|
|
74
|
+
embedder=model_b.encode, # Your query-side encoder
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Now trainer is a PhaseMLP_Trainer with a trained .mlp
|
|
78
|
+
adapter = trainer # .transform() is available after training
|
|
79
|
+
|
|
80
|
+
corpus_aligned = adapter.transform(corpus_vectors_1024d)
|
|
81
|
+
query_aligned = adapter.transform(query_vector_1024d)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Result:** R@5 = **0.70** — 91% of same-model ceiling.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### Full Qdrant Example
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from zuhut_align import ZUHUTClient
|
|
92
|
+
from sentence_transformers import SentenceTransformer
|
|
93
|
+
from qdrant_client import QdrantClient
|
|
94
|
+
from qdrant_client.models import Distance, VectorParams, PointStruct
|
|
95
|
+
|
|
96
|
+
# 1. Models
|
|
97
|
+
model_a = SentenceTransformer("BAAI/bge-large-en-v1.5") # corpus model
|
|
98
|
+
model_b = SentenceTransformer("paraphrase-MiniLM-L6-v2") # query model
|
|
99
|
+
|
|
100
|
+
# 2. ZUHUT alignment (zero-shot)
|
|
101
|
+
client = ZUHUTClient()
|
|
102
|
+
adapter = client.create_adapter(src_dim=1024)
|
|
103
|
+
|
|
104
|
+
# 3. Encode & align corpus
|
|
105
|
+
corpus_vecs = model_a.encode(corpus_docs, normalize_embeddings=True)
|
|
106
|
+
corpus_zuhut = adapter.transform(corpus_vecs) # shape (N, 384)
|
|
107
|
+
|
|
108
|
+
# 4. Index into Qdrant (384d, one-time)
|
|
109
|
+
qdrant = QdrantClient(host="localhost", port=6333)
|
|
110
|
+
qdrant.recreate_collection("my_corpus", vectors_config=VectorParams(
|
|
111
|
+
size=384, distance=Distance.COSINE
|
|
112
|
+
))
|
|
113
|
+
qdrant.upsert("my_corpus", points=[
|
|
114
|
+
PointStruct(id=i, vector=corpus_zuhut[i].tolist())
|
|
115
|
+
for i in range(len(corpus_docs))
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
# 5. Search with cheap query model
|
|
119
|
+
query = "how do neural networks learn?"
|
|
120
|
+
q_vec = model_b.encode(query, normalize_embeddings=True)
|
|
121
|
+
q_zuhut = adapter.transform(q_vec)
|
|
122
|
+
|
|
123
|
+
results = qdrant.query_points("my_corpus", query=q_zuhut[0].tolist(), limit=5)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## How It Works
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Source Model (1024d)
|
|
132
|
+
↓
|
|
133
|
+
SVD Projection → 384d
|
|
134
|
+
↓
|
|
135
|
+
Procrustes Rotation ← synthetic anchors (N=250, SEED=42)
|
|
136
|
+
↓
|
|
137
|
+
L2 Normalize
|
|
138
|
+
↓
|
|
139
|
+
[PhaseMLP] ← optional, trained on your corpus pseudo-pairs
|
|
140
|
+
↓
|
|
141
|
+
Target Space (384d) — compatible with your indexed corpus
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Zero-Shot (Level 1)
|
|
145
|
+
- Generates 250 synthetic Gaussian anchor pairs
|
|
146
|
+
- Learns an SVD projection (dim reduction) + Procrustes rotation
|
|
147
|
+
- No real data, no training, reproducible across runs
|
|
148
|
+
|
|
149
|
+
### PhaseMLP (Level 2)
|
|
150
|
+
- Residual MLP (x + net(x)) with zero-init last layer
|
|
151
|
+
- Trained with InfoNCE contrastive loss on corpus pseudo-pairs
|
|
152
|
+
- Pseudo-pairs via word dropout (10% random word removal)
|
|
153
|
+
- Training invariant to corpus size: 200–500 docs → 5 min on CPU
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Benchmark Results
|
|
158
|
+
|
|
159
|
+
Tested on Qdrant with BGE-large (corpus) vs MiniLM (query):
|
|
160
|
+
|
|
161
|
+
| Condition | R@5 | vs Ceiling |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| Same-model baseline (ceiling) | 0.767 | — |
|
|
164
|
+
| Cross-model, no alignment | 0.133 | -63.3% |
|
|
165
|
+
| **+ ZUHUT ZeroShot (Level 1)** | **0.400** | **-36.7%** |
|
|
166
|
+
| **+ ZUHUT PhaseMLP v3 (Level 2)** | **0.700** | **-6.7%** |
|
|
167
|
+
|
|
168
|
+
Vector databases tested: Qdrant ✓, FAISS ✓, LanceDB ✓, ChromaDB ✓
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Tiers
|
|
173
|
+
|
|
174
|
+
| | Level 1 ZeroShot | Level 2 PhaseMLP | Level 3 Engine |
|
|
175
|
+
|---|---|---|---|
|
|
176
|
+
| **Price** | Free | Paid | Enterprise |
|
|
177
|
+
| **Training data** | None | 200–500 docs | None |
|
|
178
|
+
| **Training time** | 0s | ~5 min CPU | 0s |
|
|
179
|
+
| **R@5 gain** | +30% | +57% | +57% |
|
|
180
|
+
| **Inference speed** | Fast (NumPy) | Fast (PyTorch) | Ultra (Rust) |
|
|
181
|
+
| **API key** | Not required | Required | License key |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Requirements
|
|
186
|
+
|
|
187
|
+
- Python ≥ 3.9
|
|
188
|
+
- numpy ≥ 1.24
|
|
189
|
+
- scipy ≥ 1.10
|
|
190
|
+
- torch ≥ 2.0 *(Level 2 only)*
|
|
191
|
+
- sentence-transformers ≥ 2.7 *(Level 2 only)*
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
Apache 2.0 — Python SDK is open source.
|
|
198
|
+
The Rust engine (`zuhri_engine`) is proprietary (Level 3 / Enterprise).
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
- 🌐 Website: [zuhut.ai](https://zuhut.ai) *(coming soon)*
|
|
205
|
+
- 📖 Docs: [docs.zuhut.ai](https://docs.zuhut.ai) *(coming soon)*
|
|
206
|
+
- 📧 Enterprise: contact@zuhut.ai
|
|
207
|
+
- 🐛 Issues: [github.com/zuhut-ai/zuhut-align/issues](https://github.com/zuhut-ai/zuhut-align/issues)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ZUHUT Align — Cross-model embedding alignment SDK.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from setuptools import setup, find_packages
|
|
6
|
+
|
|
7
|
+
with open("README.md", encoding="utf-8") as f:
|
|
8
|
+
long_description = f.read()
|
|
9
|
+
|
|
10
|
+
setup(
|
|
11
|
+
name="zuhut-align",
|
|
12
|
+
version="0.1.0",
|
|
13
|
+
author="ZUHUT",
|
|
14
|
+
author_email="contact@zuhut.ai",
|
|
15
|
+
description="Cross-model embedding alignment: bridge any two embedding models, zero-shot.",
|
|
16
|
+
long_description=long_description,
|
|
17
|
+
long_description_content_type="text/markdown",
|
|
18
|
+
url="https://github.com/zuhut-ai/zuhut-align",
|
|
19
|
+
packages=find_packages(),
|
|
20
|
+
python_requires=">=3.9",
|
|
21
|
+
|
|
22
|
+
# Core dependencies (Level 1 — ZeroShot, free)
|
|
23
|
+
install_requires=[
|
|
24
|
+
"numpy>=1.24",
|
|
25
|
+
"scipy>=1.10",
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
# Optional extras
|
|
29
|
+
extras_require={
|
|
30
|
+
# Level 2 — PhaseMLP training + inference
|
|
31
|
+
"mlp": [
|
|
32
|
+
"torch>=2.0",
|
|
33
|
+
"sentence-transformers>=2.7",
|
|
34
|
+
],
|
|
35
|
+
# Level 2 + Qdrant integration example
|
|
36
|
+
"qdrant": [
|
|
37
|
+
"torch>=2.0",
|
|
38
|
+
"sentence-transformers>=2.7",
|
|
39
|
+
"qdrant-client>=1.9",
|
|
40
|
+
],
|
|
41
|
+
# Full install (everything except engine)
|
|
42
|
+
"full": [
|
|
43
|
+
"torch>=2.0",
|
|
44
|
+
"sentence-transformers>=2.7",
|
|
45
|
+
"qdrant-client>=1.9",
|
|
46
|
+
],
|
|
47
|
+
# Development
|
|
48
|
+
"dev": [
|
|
49
|
+
"pytest>=7.0",
|
|
50
|
+
"pytest-cov",
|
|
51
|
+
"black",
|
|
52
|
+
"ruff",
|
|
53
|
+
"mypy",
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
classifiers=[
|
|
58
|
+
"Development Status :: 3 - Alpha",
|
|
59
|
+
"Intended Audience :: Developers",
|
|
60
|
+
"Intended Audience :: Science/Research",
|
|
61
|
+
"License :: OSI Approved :: Apache Software License",
|
|
62
|
+
"Programming Language :: Python :: 3",
|
|
63
|
+
"Programming Language :: Python :: 3.9",
|
|
64
|
+
"Programming Language :: Python :: 3.10",
|
|
65
|
+
"Programming Language :: Python :: 3.11",
|
|
66
|
+
"Programming Language :: Python :: 3.12",
|
|
67
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
68
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
69
|
+
],
|
|
70
|
+
|
|
71
|
+
keywords=[
|
|
72
|
+
"embeddings", "alignment", "cross-model", "retrieval",
|
|
73
|
+
"vector search", "semantic search", "sentence transformers",
|
|
74
|
+
"zero-shot", "contrastive learning",
|
|
75
|
+
],
|
|
76
|
+
|
|
77
|
+
project_urls={
|
|
78
|
+
"Documentation": "https://docs.zuhut.ai",
|
|
79
|
+
"Homepage": "https://zuhut.ai",
|
|
80
|
+
"Bug Reports": "https://github.com/zuhut-ai/zuhut-align/issues",
|
|
81
|
+
"Source": "https://github.com/zuhut-ai/zuhut-align",
|
|
82
|
+
},
|
|
83
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
zuhut-align
|
|
3
|
+
===========
|
|
4
|
+
Cross-model embedding alignment SDK.
|
|
5
|
+
|
|
6
|
+
Bridge any two embedding models with zero-shot alignment or
|
|
7
|
+
self-supervised contrastive fine-tuning.
|
|
8
|
+
|
|
9
|
+
Quick start
|
|
10
|
+
-----------
|
|
11
|
+
>>> from zuhut_align import ZUHUTClient
|
|
12
|
+
>>> client = ZUHUTClient()
|
|
13
|
+
>>> adapter = client.create_adapter(src_dim=1024)
|
|
14
|
+
>>> aligned = adapter.transform(my_vectors) # shape (N, 384)
|
|
15
|
+
|
|
16
|
+
Tiers
|
|
17
|
+
-----
|
|
18
|
+
Level 1 — ZeroShot (free)
|
|
19
|
+
No training data. Works out-of-the-box.
|
|
20
|
+
client.create_adapter(src_dim=..., tier='zeroshot')
|
|
21
|
+
|
|
22
|
+
Level 2 — PhaseMLP (paid)
|
|
23
|
+
Self-supervised on your own corpus (~5 min, 200-500 docs).
|
|
24
|
+
91% of same-model retrieval ceiling. Requires api_key.
|
|
25
|
+
client.create_adapter(src_dim=..., tier='phasemlp')
|
|
26
|
+
|
|
27
|
+
Level 3 — Rust Engine (enterprise)
|
|
28
|
+
10–50x faster inference. Requires license_key.
|
|
29
|
+
client.create_adapter(src_dim=..., tier='engine')
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from .client import ZUHUTClient
|
|
33
|
+
from .adapters.zero_shot import PureZeroShotAdapter
|
|
34
|
+
from .mlp.phase_mlp import PhaseMLP, PhaseMLP_Trainer, PhaseMLP_Adapter
|
|
35
|
+
from .utils.normalize import l2_normalize
|
|
36
|
+
|
|
37
|
+
__version__ = "0.1.0"
|
|
38
|
+
__author__ = "ZUHUT"
|
|
39
|
+
__email__ = "contact@zuhut.ai"
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
# Main entry point
|
|
43
|
+
"ZUHUTClient",
|
|
44
|
+
# Level 1 — ZeroShot
|
|
45
|
+
"PureZeroShotAdapter",
|
|
46
|
+
# Level 2 — PhaseMLP
|
|
47
|
+
"PhaseMLP",
|
|
48
|
+
"PhaseMLP_Trainer",
|
|
49
|
+
"PhaseMLP_Adapter",
|
|
50
|
+
# Utilities
|
|
51
|
+
"l2_normalize",
|
|
52
|
+
]
|