fastembed-gpu 0.3.6__tar.gz → 0.4.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.
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/PKG-INFO +3 -3
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/README.md +1 -1
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/common/model_management.py +53 -28
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/common/onnx_model.py +16 -4
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/common/utils.py +14 -2
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/image_embedding.py +8 -5
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/image_embedding_base.py +1 -1
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/onnx_embedding.py +59 -10
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/onnx_image_model.py +33 -15
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/late_interaction/colbert.py +64 -11
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/late_interaction/late_interaction_text_embedding.py +23 -17
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/parallel_processor.py +46 -8
- fastembed_gpu-0.4.0/fastembed/rerank/cross_encoder/__init__.py +3 -0
- fastembed_gpu-0.4.0/fastembed/rerank/cross_encoder/onnx_text_cross_encoder.py +147 -0
- fastembed_gpu-0.4.0/fastembed/rerank/cross_encoder/onnx_text_model.py +70 -0
- fastembed_gpu-0.4.0/fastembed/rerank/cross_encoder/text_cross_encoder.py +87 -0
- fastembed_gpu-0.4.0/fastembed/rerank/cross_encoder/text_cross_encoder_base.py +37 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/bm25.py +28 -13
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/bm42.py +64 -17
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/sparse_text_embedding.py +7 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/splade_pp.py +59 -12
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/utils/tokenizer.py +9 -2
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/clip_embedding.py +10 -5
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/e5_onnx_embedding.py +12 -4
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/onnx_embedding.py +79 -25
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/onnx_text_model.py +30 -10
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/pooled_embedding.py +20 -15
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/pooled_normalized_embedding.py +22 -10
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/text_embedding.py +10 -7
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/pyproject.toml +2 -2
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/LICENSE +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/__init__.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/common/__init__.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/common/preprocessor_utils.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/common/types.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/embedding.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/__init__.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/transform/functional.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/image/transform/operators.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/late_interaction/__init__.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/late_interaction/late_interaction_embedding_base.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/__init__.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/sparse/sparse_embedding_base.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/__init__.py +0 -0
- {fastembed_gpu-0.3.6 → fastembed_gpu-0.4.0}/fastembed/text/text_embedding_base.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastembed-gpu
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Fast, light, accurate library built for retrieval embedding generation
|
|
5
5
|
Home-page: https://github.com/qdrant/fastembed
|
|
6
6
|
License: Apache License
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Requires-Dist: PyStemmer (>=2.2.0,<3.0.0)
|
|
19
19
|
Requires-Dist: huggingface-hub (>=0.20,<1.0)
|
|
20
20
|
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
|
21
|
-
Requires-Dist: mmh3 (>=4.0,<5.0)
|
|
21
|
+
Requires-Dist: mmh3 (>=4.1.0,<5.0.0)
|
|
22
22
|
Requires-Dist: numpy (>=1.21,<2) ; python_version < "3.12"
|
|
23
23
|
Requires-Dist: numpy (>=1.26,<2) ; python_version >= "3.12"
|
|
24
24
|
Requires-Dist: onnxruntime-gpu (>=1.17.0,<2.0.0)
|
|
@@ -179,7 +179,7 @@ It requires installation of the `fastembed-gpu` package.
|
|
|
179
179
|
pip install fastembed-gpu
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
Check our [example](https://qdrant.github.io/fastembed/examples/FastEmbed_GPU/) for detailed instructions
|
|
182
|
+
Check our [example](https://qdrant.github.io/fastembed/examples/FastEmbed_GPU/) for detailed instructions, CUDA 12.x support and troubleshooting of the common issues.
|
|
183
183
|
|
|
184
184
|
```python
|
|
185
185
|
from fastembed import TextEmbedding
|
|
@@ -147,7 +147,7 @@ It requires installation of the `fastembed-gpu` package.
|
|
|
147
147
|
pip install fastembed-gpu
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
Check our [example](https://qdrant.github.io/fastembed/examples/FastEmbed_GPU/) for detailed instructions
|
|
150
|
+
Check our [example](https://qdrant.github.io/fastembed/examples/FastEmbed_GPU/) for detailed instructions, CUDA 12.x support and troubleshooting of the common issues.
|
|
151
151
|
|
|
152
152
|
```python
|
|
153
153
|
from fastembed import TextEmbedding
|
|
@@ -74,7 +74,7 @@ class ModelManagement:
|
|
|
74
74
|
if total_size_in_bytes == 0:
|
|
75
75
|
print(f"Warning: Content-length header is missing or zero in the response from {url}.")
|
|
76
76
|
|
|
77
|
-
show_progress = total_size_in_bytes and show_progress
|
|
77
|
+
show_progress = bool(total_size_in_bytes and show_progress)
|
|
78
78
|
|
|
79
79
|
with tqdm(
|
|
80
80
|
total=total_size_in_bytes,
|
|
@@ -95,6 +95,7 @@ class ModelManagement:
|
|
|
95
95
|
hf_source_repo: str,
|
|
96
96
|
cache_dir: Optional[str] = None,
|
|
97
97
|
extra_patterns: Optional[List[str]] = None,
|
|
98
|
+
local_files_only: bool = False,
|
|
98
99
|
**kwargs,
|
|
99
100
|
) -> str:
|
|
100
101
|
"""
|
|
@@ -104,6 +105,7 @@ class ModelManagement:
|
|
|
104
105
|
cache_dir (Optional[str]): The path to the cache directory.
|
|
105
106
|
extra_patterns (Optional[List[str]]): extra patterns to allow in the snapshot download, typically
|
|
106
107
|
includes the required model files.
|
|
108
|
+
local_files_only (bool, optional): Whether to only use local files. Defaults to False.
|
|
107
109
|
Returns:
|
|
108
110
|
Path: The path to the model directory.
|
|
109
111
|
"""
|
|
@@ -121,7 +123,8 @@ class ModelManagement:
|
|
|
121
123
|
repo_id=hf_source_repo,
|
|
122
124
|
allow_patterns=allow_patterns,
|
|
123
125
|
cache_dir=cache_dir,
|
|
124
|
-
local_files_only=
|
|
126
|
+
local_files_only=local_files_only,
|
|
127
|
+
**kwargs,
|
|
125
128
|
)
|
|
126
129
|
|
|
127
130
|
@classmethod
|
|
@@ -162,9 +165,10 @@ class ModelManagement:
|
|
|
162
165
|
return cache_dir
|
|
163
166
|
|
|
164
167
|
@classmethod
|
|
165
|
-
def retrieve_model_gcs(
|
|
168
|
+
def retrieve_model_gcs(
|
|
169
|
+
cls, model_name: str, source_url: str, cache_dir: str, local_files_only: bool = False
|
|
170
|
+
) -> Path:
|
|
166
171
|
fast_model_name = f"fast-{model_name.split('/')[-1]}"
|
|
167
|
-
|
|
168
172
|
cache_tmp_dir = Path(cache_dir) / "tmp"
|
|
169
173
|
model_tmp_dir = cache_tmp_dir / fast_model_name
|
|
170
174
|
model_dir = Path(cache_dir) / fast_model_name
|
|
@@ -183,22 +187,32 @@ class ModelManagement:
|
|
|
183
187
|
if model_tar_gz.exists():
|
|
184
188
|
model_tar_gz.unlink()
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
if not local_files_only:
|
|
191
|
+
cls.download_file_from_gcs(
|
|
192
|
+
source_url,
|
|
193
|
+
output_path=str(model_tar_gz),
|
|
194
|
+
)
|
|
190
195
|
|
|
191
|
-
|
|
192
|
-
|
|
196
|
+
cls.decompress_to_cache(targz_path=str(model_tar_gz), cache_dir=str(cache_tmp_dir))
|
|
197
|
+
assert model_tmp_dir.exists(), f"Could not find {model_tmp_dir} in {cache_tmp_dir}"
|
|
193
198
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
model_tar_gz.unlink()
|
|
200
|
+
# Rename from tmp to final name is atomic
|
|
201
|
+
model_tmp_dir.rename(model_dir)
|
|
202
|
+
else:
|
|
203
|
+
logger.error(
|
|
204
|
+
f"Could not find the model tar.gz file at {model_dir} and local_files_only=True."
|
|
205
|
+
)
|
|
206
|
+
raise ValueError(
|
|
207
|
+
f"Could not find the model tar.gz file at {model_dir} and local_files_only=True."
|
|
208
|
+
)
|
|
197
209
|
|
|
198
210
|
return model_dir
|
|
199
211
|
|
|
200
212
|
@classmethod
|
|
201
|
-
def download_model(
|
|
213
|
+
def download_model(
|
|
214
|
+
cls, model: Dict[str, Any], cache_dir: Path, retries: int = 3, **kwargs
|
|
215
|
+
) -> Path:
|
|
202
216
|
"""
|
|
203
217
|
Downloads a model from HuggingFace Hub or Google Cloud Storage.
|
|
204
218
|
|
|
@@ -223,7 +237,8 @@ class ModelManagement:
|
|
|
223
237
|
Returns:
|
|
224
238
|
Path: The path to the downloaded model directory.
|
|
225
239
|
"""
|
|
226
|
-
|
|
240
|
+
local_files_only = kwargs.get("local_files_only", False)
|
|
241
|
+
retries = 1 if local_files_only else retries
|
|
227
242
|
hf_source = model.get("sources", {}).get("hf")
|
|
228
243
|
url_source = model.get("sources", {}).get("url")
|
|
229
244
|
|
|
@@ -241,24 +256,34 @@ class ModelManagement:
|
|
|
241
256
|
hf_source,
|
|
242
257
|
cache_dir=str(cache_dir),
|
|
243
258
|
extra_patterns=extra_patterns,
|
|
244
|
-
|
|
259
|
+
**kwargs,
|
|
245
260
|
)
|
|
246
261
|
)
|
|
247
262
|
except (EnvironmentError, RepositoryNotFoundError, ValueError) as e:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
263
|
+
if not local_files_only:
|
|
264
|
+
logger.error(
|
|
265
|
+
f"Could not download model from HuggingFace: {e} "
|
|
266
|
+
"Falling back to other sources."
|
|
267
|
+
)
|
|
268
|
+
if url_source or local_files_only:
|
|
253
269
|
try:
|
|
254
|
-
return cls.retrieve_model_gcs(
|
|
270
|
+
return cls.retrieve_model_gcs(
|
|
271
|
+
model["model"],
|
|
272
|
+
url_source,
|
|
273
|
+
str(cache_dir),
|
|
274
|
+
local_files_only=local_files_only,
|
|
275
|
+
)
|
|
255
276
|
except Exception:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
277
|
+
if not local_files_only:
|
|
278
|
+
logger.error(f"Could not download model from url: {url_source}")
|
|
279
|
+
|
|
280
|
+
if local_files_only:
|
|
281
|
+
logger.error("Could not find model in cache_dir")
|
|
282
|
+
else:
|
|
283
|
+
logger.error(
|
|
284
|
+
f"Could not download model from either source, sleeping for {sleep} seconds, {retries} retries left."
|
|
285
|
+
)
|
|
261
286
|
time.sleep(sleep)
|
|
262
287
|
sleep *= 3
|
|
263
288
|
|
|
264
|
-
raise ValueError(f"Could not
|
|
289
|
+
raise ValueError(f"Could not load model {model['model']} from any source.")
|
|
@@ -50,19 +50,28 @@ class OnnxModel(Generic[T]):
|
|
|
50
50
|
"""
|
|
51
51
|
return onnx_input
|
|
52
52
|
|
|
53
|
-
def
|
|
53
|
+
def _load_onnx_model(
|
|
54
54
|
self,
|
|
55
55
|
model_dir: Path,
|
|
56
56
|
model_file: str,
|
|
57
57
|
threads: Optional[int],
|
|
58
58
|
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
59
|
+
cuda: bool = False,
|
|
60
|
+
device_id: Optional[int] = None,
|
|
59
61
|
) -> None:
|
|
60
62
|
model_path = model_dir / model_file
|
|
61
63
|
# List of Execution Providers: https://onnxruntime.ai/docs/execution-providers
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
if providers is not None:
|
|
66
|
+
onnx_providers = list(providers)
|
|
67
|
+
elif cuda:
|
|
68
|
+
if device_id is None:
|
|
69
|
+
onnx_providers = ["CUDAExecutionProvider"]
|
|
70
|
+
else:
|
|
71
|
+
onnx_providers = [("CUDAExecutionProvider", {"device_id": device_id})]
|
|
72
|
+
else:
|
|
73
|
+
onnx_providers = ["CPUExecutionProvider"]
|
|
74
|
+
|
|
66
75
|
available_providers = ort.get_available_providers()
|
|
67
76
|
requested_provider_names = []
|
|
68
77
|
for provider in onnx_providers:
|
|
@@ -94,6 +103,9 @@ class OnnxModel(Generic[T]):
|
|
|
94
103
|
RuntimeWarning,
|
|
95
104
|
)
|
|
96
105
|
|
|
106
|
+
def load_onnx_model(self) -> None:
|
|
107
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
108
|
+
|
|
97
109
|
def onnx_embed(self, *args, **kwargs) -> OnnxOutputContext:
|
|
98
110
|
raise NotImplementedError("Subclasses must implement this method")
|
|
99
111
|
|
|
@@ -3,8 +3,11 @@ import tempfile
|
|
|
3
3
|
from itertools import islice
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Generator, Iterable, Optional, Union
|
|
6
|
-
|
|
6
|
+
import unicodedata
|
|
7
|
+
import sys
|
|
7
8
|
import numpy as np
|
|
9
|
+
import re
|
|
10
|
+
from typing import Set
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
def normalize(input_array, p=2, dim=1, eps=1e-12) -> np.ndarray:
|
|
@@ -37,7 +40,16 @@ def define_cache_dir(cache_dir: Optional[str] = None) -> Path:
|
|
|
37
40
|
cache_path = Path(os.getenv("FASTEMBED_CACHE_PATH", default_cache_dir))
|
|
38
41
|
else:
|
|
39
42
|
cache_path = Path(cache_dir)
|
|
40
|
-
|
|
41
43
|
cache_path.mkdir(parents=True, exist_ok=True)
|
|
42
44
|
|
|
43
45
|
return cache_path
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_all_punctuation() -> Set[str]:
|
|
49
|
+
return set(
|
|
50
|
+
chr(i) for i in range(sys.maxunicode) if unicodedata.category(chr(i)).startswith("P")
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def remove_non_alphanumeric(text: str) -> str:
|
|
55
|
+
return re.sub(r"[^\w\s]", " ", text, flags=re.UNICODE)
|
|
@@ -25,6 +25,7 @@ class ImageEmbedding(ImageEmbeddingBase):
|
|
|
25
25
|
"model": "Qdrant/clip-ViT-B-32-vision",
|
|
26
26
|
"dim": 512,
|
|
27
27
|
"description": "CLIP vision encoder based on ViT-B/32",
|
|
28
|
+
"license": "mit",
|
|
28
29
|
"size_in_GB": 0.33,
|
|
29
30
|
"sources": {
|
|
30
31
|
"hf": "Qdrant/clip-ViT-B-32-vision",
|
|
@@ -45,21 +46,23 @@ class ImageEmbedding(ImageEmbeddingBase):
|
|
|
45
46
|
cache_dir: Optional[str] = None,
|
|
46
47
|
threads: Optional[int] = None,
|
|
47
48
|
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
49
|
+
cuda: bool = False,
|
|
50
|
+
device_ids: Optional[List[int]] = None,
|
|
51
|
+
lazy_load: bool = False,
|
|
48
52
|
**kwargs,
|
|
49
53
|
):
|
|
50
54
|
super().__init__(model_name, cache_dir, threads, **kwargs)
|
|
51
|
-
|
|
52
55
|
for EMBEDDING_MODEL_TYPE in self.EMBEDDINGS_REGISTRY:
|
|
53
56
|
supported_models = EMBEDDING_MODEL_TYPE.list_supported_models()
|
|
54
|
-
if any(
|
|
55
|
-
model_name.lower() == model["model"].lower()
|
|
56
|
-
for model in supported_models
|
|
57
|
-
):
|
|
57
|
+
if any(model_name.lower() == model["model"].lower() for model in supported_models):
|
|
58
58
|
self.model = EMBEDDING_MODEL_TYPE(
|
|
59
59
|
model_name,
|
|
60
60
|
cache_dir,
|
|
61
61
|
threads=threads,
|
|
62
62
|
providers=providers,
|
|
63
|
+
cuda=cuda,
|
|
64
|
+
device_ids=device_ids,
|
|
65
|
+
lazy_load=lazy_load,
|
|
63
66
|
**kwargs,
|
|
64
67
|
)
|
|
65
68
|
return
|
|
@@ -30,7 +30,7 @@ class ImageEmbeddingBase(ModelManagement):
|
|
|
30
30
|
Embeds a list of images into a list of embeddings.
|
|
31
31
|
|
|
32
32
|
Args:
|
|
33
|
-
images
|
|
33
|
+
images: The list of image paths to preprocess and embed.
|
|
34
34
|
batch_size: Batch size for encoding
|
|
35
35
|
parallel:
|
|
36
36
|
If > 1, data-parallel encoding will be used, recommended for offline encoding of large datasets.
|
|
@@ -13,6 +13,7 @@ supported_onnx_models = [
|
|
|
13
13
|
"model": "Qdrant/clip-ViT-B-32-vision",
|
|
14
14
|
"dim": 512,
|
|
15
15
|
"description": "Image embeddings, Multimodal (text&image), 2021 year",
|
|
16
|
+
"license": "mit",
|
|
16
17
|
"size_in_GB": 0.34,
|
|
17
18
|
"sources": {
|
|
18
19
|
"hf": "Qdrant/clip-ViT-B-32-vision",
|
|
@@ -23,6 +24,7 @@ supported_onnx_models = [
|
|
|
23
24
|
"model": "Qdrant/resnet50-onnx",
|
|
24
25
|
"dim": 2048,
|
|
25
26
|
"description": "Image embeddings, Unimodal (image), 2016 year",
|
|
27
|
+
"license": "apache-2.0",
|
|
26
28
|
"size_in_GB": 0.1,
|
|
27
29
|
"sources": {
|
|
28
30
|
"hf": "Qdrant/resnet50-onnx",
|
|
@@ -33,6 +35,7 @@ supported_onnx_models = [
|
|
|
33
35
|
"model": "Qdrant/Unicom-ViT-B-16",
|
|
34
36
|
"dim": 768,
|
|
35
37
|
"description": "Image embeddings (more detailed than Unicom-ViT-B-32), Multimodal (text&image), 2023 year",
|
|
38
|
+
"license": "apache-2.0",
|
|
36
39
|
"size_in_GB": 0.82,
|
|
37
40
|
"sources": {
|
|
38
41
|
"hf": "Qdrant/Unicom-ViT-B-16",
|
|
@@ -43,6 +46,7 @@ supported_onnx_models = [
|
|
|
43
46
|
"model": "Qdrant/Unicom-ViT-B-32",
|
|
44
47
|
"dim": 512,
|
|
45
48
|
"description": "Image embeddings, Multimodal (text&image), 2023 year",
|
|
49
|
+
"license": "apache-2.0",
|
|
46
50
|
"size_in_GB": 0.48,
|
|
47
51
|
"sources": {
|
|
48
52
|
"hf": "Qdrant/Unicom-ViT-B-32",
|
|
@@ -59,6 +63,10 @@ class OnnxImageEmbedding(ImageEmbeddingBase, OnnxImageModel[np.ndarray]):
|
|
|
59
63
|
cache_dir: Optional[str] = None,
|
|
60
64
|
threads: Optional[int] = None,
|
|
61
65
|
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
66
|
+
cuda: bool = False,
|
|
67
|
+
device_ids: Optional[List[int]] = None,
|
|
68
|
+
lazy_load: bool = False,
|
|
69
|
+
device_id: Optional[int] = None,
|
|
62
70
|
**kwargs,
|
|
63
71
|
):
|
|
64
72
|
"""
|
|
@@ -68,24 +76,56 @@ class OnnxImageEmbedding(ImageEmbeddingBase, OnnxImageModel[np.ndarray]):
|
|
|
68
76
|
Can be set using the `FASTEMBED_CACHE_PATH` env variable.
|
|
69
77
|
Defaults to `fastembed_cache` in the system's temp directory.
|
|
70
78
|
threads (int, optional): The number of threads single onnxruntime session can use. Defaults to None.
|
|
79
|
+
providers (Optional[Sequence[OnnxProvider]], optional): The list of onnxruntime providers to use.
|
|
80
|
+
Mutually exclusive with the `cuda` and `device_ids` arguments. Defaults to None.
|
|
81
|
+
cuda (bool, optional): Whether to use cuda for inference. Mutually exclusive with `providers`
|
|
82
|
+
Defaults to False.
|
|
83
|
+
device_ids (Optional[List[int]], optional): The list of device ids to use for data parallel processing in
|
|
84
|
+
workers. Should be used with `cuda=True`, mutually exclusive with `providers`. Defaults to None.
|
|
85
|
+
lazy_load (bool, optional): Whether to load the model during class initialization or on demand.
|
|
86
|
+
Should be set to True when using multiple-gpu and parallel encoding. Defaults to False.
|
|
87
|
+
device_id (Optional[int], optional): The device id to use for loading the model in the worker process.
|
|
71
88
|
|
|
72
89
|
Raises:
|
|
73
90
|
ValueError: If the model_name is not in the format <org>/<model> e.g. BAAI/bge-base-en.
|
|
74
91
|
"""
|
|
75
92
|
|
|
76
93
|
super().__init__(model_name, cache_dir, threads, **kwargs)
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
self.providers = providers
|
|
95
|
+
self.lazy_load = lazy_load
|
|
96
|
+
|
|
97
|
+
# List of device ids, that can be used for data parallel processing in workers
|
|
98
|
+
self.device_ids = device_ids
|
|
99
|
+
self.cuda = cuda
|
|
100
|
+
|
|
101
|
+
# This device_id will be used if we need to load model in current process
|
|
102
|
+
if device_id is not None:
|
|
103
|
+
self.device_id = device_id
|
|
104
|
+
elif self.device_ids is not None:
|
|
105
|
+
self.device_id = self.device_ids[0]
|
|
106
|
+
else:
|
|
107
|
+
self.device_id = None
|
|
108
|
+
|
|
109
|
+
self.model_description = self._get_model_description(model_name)
|
|
79
110
|
self.cache_dir = define_cache_dir(cache_dir)
|
|
80
|
-
|
|
81
|
-
model_description, self.cache_dir, local_files_only=self._local_files_only
|
|
111
|
+
self._model_dir = self.download_model(
|
|
112
|
+
self.model_description, self.cache_dir, local_files_only=self._local_files_only
|
|
82
113
|
)
|
|
83
114
|
|
|
84
|
-
self.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
if not self.lazy_load:
|
|
116
|
+
self.load_onnx_model()
|
|
117
|
+
|
|
118
|
+
def load_onnx_model(self) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Load the onnx model.
|
|
121
|
+
"""
|
|
122
|
+
self._load_onnx_model(
|
|
123
|
+
model_dir=self._model_dir,
|
|
124
|
+
model_file=self.model_description["model_file"],
|
|
125
|
+
threads=self.threads,
|
|
126
|
+
providers=self.providers,
|
|
127
|
+
cuda=self.cuda,
|
|
128
|
+
device_id=self.device_id,
|
|
89
129
|
)
|
|
90
130
|
|
|
91
131
|
@classmethod
|
|
@@ -120,12 +160,16 @@ class OnnxImageEmbedding(ImageEmbeddingBase, OnnxImageModel[np.ndarray]):
|
|
|
120
160
|
Returns:
|
|
121
161
|
List of embeddings, one per document
|
|
122
162
|
"""
|
|
163
|
+
|
|
123
164
|
yield from self._embed_images(
|
|
124
165
|
model_name=self.model_name,
|
|
125
166
|
cache_dir=str(self.cache_dir),
|
|
126
167
|
images=images,
|
|
127
168
|
batch_size=batch_size,
|
|
128
169
|
parallel=parallel,
|
|
170
|
+
providers=self.providers,
|
|
171
|
+
cuda=self.cuda,
|
|
172
|
+
device_ids=self.device_ids,
|
|
129
173
|
**kwargs,
|
|
130
174
|
)
|
|
131
175
|
|
|
@@ -148,4 +192,9 @@ class OnnxImageEmbedding(ImageEmbeddingBase, OnnxImageModel[np.ndarray]):
|
|
|
148
192
|
|
|
149
193
|
class OnnxImageEmbeddingWorker(ImageEmbeddingWorker):
|
|
150
194
|
def init_embedding(self, model_name: str, cache_dir: str, **kwargs) -> OnnxImageEmbedding:
|
|
151
|
-
return OnnxImageEmbedding(
|
|
195
|
+
return OnnxImageEmbedding(
|
|
196
|
+
model_name=model_name,
|
|
197
|
+
cache_dir=cache_dir,
|
|
198
|
+
threads=1,
|
|
199
|
+
**kwargs,
|
|
200
|
+
)
|
|
@@ -36,21 +36,28 @@ class OnnxImageModel(OnnxModel[T]):
|
|
|
36
36
|
"""
|
|
37
37
|
return onnx_input
|
|
38
38
|
|
|
39
|
-
def
|
|
39
|
+
def _load_onnx_model(
|
|
40
40
|
self,
|
|
41
41
|
model_dir: Path,
|
|
42
42
|
model_file: str,
|
|
43
43
|
threads: Optional[int],
|
|
44
44
|
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
45
|
+
cuda: bool = False,
|
|
46
|
+
device_id: Optional[int] = None,
|
|
45
47
|
) -> None:
|
|
46
|
-
super().
|
|
48
|
+
super()._load_onnx_model(
|
|
47
49
|
model_dir=model_dir,
|
|
48
50
|
model_file=model_file,
|
|
49
51
|
threads=threads,
|
|
50
52
|
providers=providers,
|
|
53
|
+
cuda=cuda,
|
|
54
|
+
device_id=device_id,
|
|
51
55
|
)
|
|
52
56
|
self.processor = load_preprocessor(model_dir=model_dir)
|
|
53
57
|
|
|
58
|
+
def load_onnx_model(self) -> None:
|
|
59
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
60
|
+
|
|
54
61
|
def _build_onnx_input(self, encoded: np.ndarray) -> Dict[str, np.ndarray]:
|
|
55
62
|
return {node.name: encoded for node in self.model.get_inputs()}
|
|
56
63
|
|
|
@@ -74,33 +81,44 @@ class OnnxImageModel(OnnxModel[T]):
|
|
|
74
81
|
images: ImageInput,
|
|
75
82
|
batch_size: int = 256,
|
|
76
83
|
parallel: Optional[int] = None,
|
|
84
|
+
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
85
|
+
cuda: bool = False,
|
|
86
|
+
device_ids: Optional[List[int]] = None,
|
|
77
87
|
**kwargs,
|
|
78
88
|
) -> Iterable[T]:
|
|
79
89
|
is_small = False
|
|
80
90
|
|
|
81
|
-
if (
|
|
82
|
-
isinstance(images, str)
|
|
83
|
-
or isinstance(images, Path)
|
|
84
|
-
or (isinstance(images, Image.Image))
|
|
85
|
-
):
|
|
91
|
+
if isinstance(images, (str, Path, Image.Image)):
|
|
86
92
|
images = [images]
|
|
87
93
|
is_small = True
|
|
88
94
|
|
|
89
|
-
if isinstance(images, list):
|
|
90
|
-
|
|
91
|
-
is_small = True
|
|
92
|
-
|
|
93
|
-
if parallel == 0:
|
|
94
|
-
parallel = os.cpu_count()
|
|
95
|
+
if isinstance(images, list) and len(images) < batch_size:
|
|
96
|
+
is_small = True
|
|
95
97
|
|
|
96
98
|
if parallel is None or is_small:
|
|
99
|
+
if not hasattr(self, "model") or self.model is None:
|
|
100
|
+
self.load_onnx_model()
|
|
101
|
+
|
|
97
102
|
for batch in iter_batch(images, batch_size):
|
|
98
103
|
yield from self._post_process_onnx_output(self.onnx_embed(batch))
|
|
99
104
|
else:
|
|
105
|
+
if parallel == 0:
|
|
106
|
+
parallel = os.cpu_count()
|
|
107
|
+
|
|
100
108
|
start_method = "forkserver" if "forkserver" in get_all_start_methods() else "spawn"
|
|
101
|
-
params = {
|
|
109
|
+
params = {
|
|
110
|
+
"model_name": model_name,
|
|
111
|
+
"cache_dir": cache_dir,
|
|
112
|
+
"providers": providers,
|
|
113
|
+
**kwargs,
|
|
114
|
+
}
|
|
115
|
+
|
|
102
116
|
pool = ParallelWorkerPool(
|
|
103
|
-
parallel
|
|
117
|
+
num_workers=parallel or 1,
|
|
118
|
+
worker=self._get_worker_class(),
|
|
119
|
+
cuda=cuda,
|
|
120
|
+
device_ids=device_ids,
|
|
121
|
+
start_method=start_method,
|
|
104
122
|
)
|
|
105
123
|
for batch in pool.ordered_map(iter_batch(images, batch_size), **params):
|
|
106
124
|
yield from self._post_process_onnx_output(batch)
|
|
@@ -17,6 +17,7 @@ supported_colbert_models = [
|
|
|
17
17
|
"model": "colbert-ir/colbertv2.0",
|
|
18
18
|
"dim": 128,
|
|
19
19
|
"description": "Late interaction model",
|
|
20
|
+
"license": "mit",
|
|
20
21
|
"size_in_GB": 0.44,
|
|
21
22
|
"sources": {
|
|
22
23
|
"hf": "colbert-ir/colbertv2.0",
|
|
@@ -27,6 +28,7 @@ supported_colbert_models = [
|
|
|
27
28
|
"model": "answerdotai/answerai-colbert-small-v1",
|
|
28
29
|
"dim": 96,
|
|
29
30
|
"description": "Text embeddings, Unimodal (text), Multilingual (~100 languages), 512 input tokens truncation, 2024 year",
|
|
31
|
+
"license": "apache-2.0",
|
|
30
32
|
"size_in_GB": 0.13,
|
|
31
33
|
"sources": {
|
|
32
34
|
"hf": "answerdotai/answerai-colbert-small-v1",
|
|
@@ -48,6 +50,11 @@ class Colbert(LateInteractionTextEmbeddingBase, OnnxTextModel[np.ndarray]):
|
|
|
48
50
|
if not is_doc:
|
|
49
51
|
return output.model_output.astype(np.float32)
|
|
50
52
|
|
|
53
|
+
if output.input_ids is None or output.attention_mask is None:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"input_ids and attention_mask must be provided for document post-processing"
|
|
56
|
+
)
|
|
57
|
+
|
|
51
58
|
for i, token_sequence in enumerate(output.input_ids):
|
|
52
59
|
for j, token_id in enumerate(token_sequence):
|
|
53
60
|
if token_id in self.skip_list or token_id == self.pad_token_id:
|
|
@@ -117,6 +124,10 @@ class Colbert(LateInteractionTextEmbeddingBase, OnnxTextModel[np.ndarray]):
|
|
|
117
124
|
cache_dir: Optional[str] = None,
|
|
118
125
|
threads: Optional[int] = None,
|
|
119
126
|
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
127
|
+
cuda: bool = False,
|
|
128
|
+
device_ids: Optional[List[int]] = None,
|
|
129
|
+
lazy_load: bool = False,
|
|
130
|
+
device_id: Optional[int] = None,
|
|
120
131
|
**kwargs,
|
|
121
132
|
):
|
|
122
133
|
"""
|
|
@@ -126,29 +137,60 @@ class Colbert(LateInteractionTextEmbeddingBase, OnnxTextModel[np.ndarray]):
|
|
|
126
137
|
Can be set using the `FASTEMBED_CACHE_PATH` env variable.
|
|
127
138
|
Defaults to `fastembed_cache` in the system's temp directory.
|
|
128
139
|
threads (int, optional): The number of threads single onnxruntime session can use. Defaults to None.
|
|
140
|
+
providers (Optional[Sequence[OnnxProvider]], optional): The list of onnxruntime providers to use.
|
|
141
|
+
Mutually exclusive with the `cuda` and `device_ids` arguments. Defaults to None.
|
|
142
|
+
cuda (bool, optional): Whether to use cuda for inference. Mutually exclusive with `providers`
|
|
143
|
+
Defaults to False.
|
|
144
|
+
device_ids (Optional[List[int]], optional): The list of device ids to use for data parallel processing in
|
|
145
|
+
workers. Should be used with `cuda=True`, mutually exclusive with `providers`. Defaults to None.
|
|
146
|
+
lazy_load (bool, optional): Whether to load the model during class initialization or on demand.
|
|
147
|
+
Should be set to True when using multiple-gpu and parallel encoding. Defaults to False.
|
|
148
|
+
device_id (Optional[int], optional): The device id to use for loading the model in the worker process.
|
|
129
149
|
|
|
130
150
|
Raises:
|
|
131
151
|
ValueError: If the model_name is not in the format <org>/<model> e.g. BAAI/bge-base-en.
|
|
132
152
|
"""
|
|
133
153
|
|
|
134
154
|
super().__init__(model_name, cache_dir, threads, **kwargs)
|
|
155
|
+
self.providers = providers
|
|
156
|
+
self.lazy_load = lazy_load
|
|
157
|
+
|
|
158
|
+
# List of device ids, that can be used for data parallel processing in workers
|
|
159
|
+
self.device_ids = device_ids
|
|
160
|
+
self.cuda = cuda
|
|
161
|
+
|
|
162
|
+
# This device_id will be used if we need to load model in current process
|
|
163
|
+
if device_id is not None:
|
|
164
|
+
self.device_id = device_id
|
|
165
|
+
elif self.device_ids is not None:
|
|
166
|
+
self.device_id = self.device_ids[0]
|
|
167
|
+
else:
|
|
168
|
+
self.device_id = None
|
|
135
169
|
|
|
136
|
-
model_description = self._get_model_description(model_name)
|
|
170
|
+
self.model_description = self._get_model_description(model_name)
|
|
137
171
|
self.cache_dir = define_cache_dir(cache_dir)
|
|
138
172
|
|
|
139
|
-
|
|
140
|
-
model_description, self.cache_dir, local_files_only=self._local_files_only
|
|
173
|
+
self._model_dir = self.download_model(
|
|
174
|
+
self.model_description, self.cache_dir, local_files_only=self._local_files_only
|
|
141
175
|
)
|
|
176
|
+
self.mask_token_id = None
|
|
177
|
+
self.pad_token_id = None
|
|
178
|
+
self.skip_list = set()
|
|
142
179
|
|
|
143
|
-
self.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
180
|
+
if not self.lazy_load:
|
|
181
|
+
self.load_onnx_model()
|
|
182
|
+
|
|
183
|
+
def load_onnx_model(self) -> None:
|
|
184
|
+
self._load_onnx_model(
|
|
185
|
+
model_dir=self._model_dir,
|
|
186
|
+
model_file=self.model_description["model_file"],
|
|
187
|
+
threads=self.threads,
|
|
188
|
+
providers=self.providers,
|
|
189
|
+
cuda=self.cuda,
|
|
190
|
+
device_id=self.device_id,
|
|
148
191
|
)
|
|
149
192
|
self.mask_token_id = self.special_token_to_id["[MASK]"]
|
|
150
193
|
self.pad_token_id = self.tokenizer.padding["pad_id"]
|
|
151
|
-
|
|
152
194
|
self.skip_list = {
|
|
153
195
|
self.tokenizer.encode(symbol, add_special_tokens=False).ids[0]
|
|
154
196
|
for symbol in string.punctuation
|
|
@@ -182,13 +224,19 @@ class Colbert(LateInteractionTextEmbeddingBase, OnnxTextModel[np.ndarray]):
|
|
|
182
224
|
documents=documents,
|
|
183
225
|
batch_size=batch_size,
|
|
184
226
|
parallel=parallel,
|
|
227
|
+
providers=self.providers,
|
|
228
|
+
cuda=self.cuda,
|
|
229
|
+
device_ids=self.device_ids,
|
|
185
230
|
**kwargs,
|
|
186
231
|
)
|
|
187
232
|
|
|
188
|
-
def query_embed(self, query: Union[str, List[str]], **kwargs) -> np.ndarray:
|
|
233
|
+
def query_embed(self, query: Union[str, List[str]], **kwargs) -> Iterable[np.ndarray]:
|
|
189
234
|
if isinstance(query, str):
|
|
190
235
|
query = [query]
|
|
191
236
|
|
|
237
|
+
if not hasattr(self, "model") or self.model is None:
|
|
238
|
+
self.load_onnx_model()
|
|
239
|
+
|
|
192
240
|
for text in query:
|
|
193
241
|
yield from self._post_process_onnx_output(
|
|
194
242
|
self.onnx_embed([text], is_doc=False), is_doc=False
|
|
@@ -201,4 +249,9 @@ class Colbert(LateInteractionTextEmbeddingBase, OnnxTextModel[np.ndarray]):
|
|
|
201
249
|
|
|
202
250
|
class ColbertEmbeddingWorker(TextEmbeddingWorker):
|
|
203
251
|
def init_embedding(self, model_name: str, cache_dir: str, **kwargs) -> Colbert:
|
|
204
|
-
return Colbert(
|
|
252
|
+
return Colbert(
|
|
253
|
+
model_name=model_name,
|
|
254
|
+
cache_dir=cache_dir,
|
|
255
|
+
threads=1,
|
|
256
|
+
**kwargs,
|
|
257
|
+
)
|