fastembed-gpu 0.2.7__tar.gz → 0.3.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.2.7 → fastembed_gpu-0.3.0}/PKG-INFO +18 -9
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/README.md +9 -4
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/__init__.py +9 -1
- fastembed_gpu-0.3.0/fastembed/common/__init__.py +3 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/common/model_management.py +1 -0
- fastembed_gpu-0.3.0/fastembed/common/onnx_model.py +113 -0
- fastembed_gpu-0.2.7/fastembed/common/models.py → fastembed_gpu-0.3.0/fastembed/common/preprocessor_utils.py +35 -15
- fastembed_gpu-0.3.0/fastembed/common/types.py +14 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/common/utils.py +10 -0
- fastembed_gpu-0.3.0/fastembed/image/__init__.py +4 -0
- fastembed_gpu-0.3.0/fastembed/image/image_embedding.py +87 -0
- fastembed_gpu-0.3.0/fastembed/image/image_embedding_base.py +39 -0
- fastembed_gpu-0.3.0/fastembed/image/onnx_embedding.py +134 -0
- fastembed_gpu-0.3.0/fastembed/image/onnx_image_model.py +107 -0
- fastembed_gpu-0.3.0/fastembed/image/transform/functional.py +125 -0
- fastembed_gpu-0.3.0/fastembed/image/transform/operators.py +168 -0
- fastembed_gpu-0.3.0/fastembed/late_interaction/__init__.py +4 -0
- fastembed_gpu-0.3.0/fastembed/late_interaction/colbert.py +196 -0
- fastembed_gpu-0.3.0/fastembed/late_interaction/late_interaction_embedding_base.py +60 -0
- fastembed_gpu-0.3.0/fastembed/late_interaction/late_interaction_text_embedding.py +104 -0
- fastembed_gpu-0.3.0/fastembed/sparse/bm42.py +281 -0
- fastembed_gpu-0.3.0/fastembed/sparse/sparse_embedding_base.py +81 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/sparse/sparse_text_embedding.py +14 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/sparse/splade_pp.py +10 -9
- fastembed_gpu-0.3.0/fastembed/text/clip_embedding.py +49 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/e5_onnx_embedding.py +2 -2
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/jina_onnx_embedding.py +8 -7
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/onnx_embedding.py +14 -13
- fastembed_gpu-0.3.0/fastembed/text/onnx_text_model.py +123 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/text_embedding.py +2 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/pyproject.toml +9 -5
- fastembed_gpu-0.2.7/fastembed/common/__init__.py +0 -3
- fastembed_gpu-0.2.7/fastembed/common/onnx_model.py +0 -165
- fastembed_gpu-0.2.7/fastembed/image/__init__.py +0 -0
- fastembed_gpu-0.2.7/fastembed/sparse/sparse_embedding_base.py +0 -44
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/LICENSE +0 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/embedding.py +0 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/parallel_processor.py +0 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/sparse/__init__.py +0 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/__init__.py +0 -0
- {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/text_embedding_base.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastembed-gpu
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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
|
|
7
7
|
Keywords: vector,embedding,neural,search,qdrant,sentence-transformers
|
|
8
|
-
Author:
|
|
9
|
-
Author-email:
|
|
8
|
+
Author: Qdrant Team
|
|
9
|
+
Author-email: info@qdrant.tech
|
|
10
10
|
Requires-Python: >=3.8.0,<3.13
|
|
11
11
|
Classifier: License :: Other/Proprietary License
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -15,13 +15,17 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Requires-Dist:
|
|
18
|
+
Requires-Dist: PyStemmer (>=2.2.0,<3.0.0)
|
|
19
|
+
Requires-Dist: huggingface-hub (>=0.20,<1.0)
|
|
19
20
|
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
|
21
|
+
Requires-Dist: mmh3 (>=4.0,<5.0)
|
|
20
22
|
Requires-Dist: numpy (>=1.21) ; python_version < "3.12"
|
|
21
23
|
Requires-Dist: numpy (>=1.26) ; python_version >= "3.12"
|
|
22
24
|
Requires-Dist: onnxruntime-gpu (>=1.17.0,<2.0.0)
|
|
25
|
+
Requires-Dist: pillow (>=10.3.0,<11.0.0)
|
|
23
26
|
Requires-Dist: requests (>=2.31,<3.0)
|
|
24
|
-
Requires-Dist:
|
|
27
|
+
Requires-Dist: snowballstemmer (>=2.2.0,<3.0.0)
|
|
28
|
+
Requires-Dist: tokenizers (>=0.15,<1.0)
|
|
25
29
|
Requires-Dist: tqdm (>=4.66,<5.0)
|
|
26
30
|
Project-URL: Repository, https://github.com/qdrant/fastembed
|
|
27
31
|
Description-Content-Type: text/markdown
|
|
@@ -78,17 +82,22 @@ len(embeddings_list[0]) # Vector of 384 dimensions
|
|
|
78
82
|
|
|
79
83
|
### ⚡️ FastEmbed on a GPU
|
|
80
84
|
|
|
81
|
-
FastEmbed supports running on GPU devices.
|
|
82
|
-
|
|
85
|
+
FastEmbed supports running on GPU devices.
|
|
86
|
+
It requires installation of the `fastembed-gpu` package.
|
|
83
87
|
|
|
84
88
|
```bash
|
|
85
89
|
pip install fastembed-gpu
|
|
86
|
-
```
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Check our [example](https://qdrant.github.io/fastembed/examples/FastEmbed_GPU/) for the detailed instructions and CUDA 12.x support.
|
|
87
93
|
|
|
88
94
|
```python
|
|
89
95
|
from fastembed import TextEmbedding
|
|
90
96
|
|
|
91
|
-
embedding_model = TextEmbedding(
|
|
97
|
+
embedding_model = TextEmbedding(
|
|
98
|
+
model_name="BAAI/bge-small-en-v1.5",
|
|
99
|
+
providers=["CUDAExecutionProvider"]
|
|
100
|
+
)
|
|
92
101
|
print("The model BAAI/bge-small-en-v1.5 is ready to use on a GPU.")
|
|
93
102
|
|
|
94
103
|
```
|
|
@@ -50,17 +50,22 @@ len(embeddings_list[0]) # Vector of 384 dimensions
|
|
|
50
50
|
|
|
51
51
|
### ⚡️ FastEmbed on a GPU
|
|
52
52
|
|
|
53
|
-
FastEmbed supports running on GPU devices.
|
|
54
|
-
|
|
53
|
+
FastEmbed supports running on GPU devices.
|
|
54
|
+
It requires installation of the `fastembed-gpu` package.
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
57
|
pip install fastembed-gpu
|
|
58
|
-
```
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Check our [example](https://qdrant.github.io/fastembed/examples/FastEmbed_GPU/) for the detailed instructions and CUDA 12.x support.
|
|
59
61
|
|
|
60
62
|
```python
|
|
61
63
|
from fastembed import TextEmbedding
|
|
62
64
|
|
|
63
|
-
embedding_model = TextEmbedding(
|
|
65
|
+
embedding_model = TextEmbedding(
|
|
66
|
+
model_name="BAAI/bge-small-en-v1.5",
|
|
67
|
+
providers=["CUDAExecutionProvider"]
|
|
68
|
+
)
|
|
64
69
|
print("The model BAAI/bge-small-en-v1.5 is ready to use on a GPU.")
|
|
65
70
|
|
|
66
71
|
```
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import importlib.metadata
|
|
2
2
|
|
|
3
|
+
from fastembed.image import ImageEmbedding
|
|
3
4
|
from fastembed.text import TextEmbedding
|
|
4
5
|
from fastembed.sparse import SparseTextEmbedding, SparseEmbedding
|
|
6
|
+
from fastembed.late_interaction import LateInteractionTextEmbedding
|
|
5
7
|
|
|
6
8
|
try:
|
|
7
9
|
version = importlib.metadata.version("fastembed")
|
|
@@ -9,4 +11,10 @@ except importlib.metadata.PackageNotFoundError as _:
|
|
|
9
11
|
version = importlib.metadata.version("fastembed-gpu")
|
|
10
12
|
|
|
11
13
|
__version__ = version
|
|
12
|
-
__all__ = [
|
|
14
|
+
__all__ = [
|
|
15
|
+
"TextEmbedding",
|
|
16
|
+
"SparseTextEmbedding",
|
|
17
|
+
"SparseEmbedding",
|
|
18
|
+
"ImageEmbedding",
|
|
19
|
+
"LateInteractionTextEmbedding",
|
|
20
|
+
]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict, Generic, Iterable, Optional, Tuple, Type, TypeVar, Sequence
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import onnxruntime as ort
|
|
8
|
+
|
|
9
|
+
from fastembed.common.types import OnnxProvider
|
|
10
|
+
from fastembed.parallel_processor import Worker
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Holds type of the embedding result
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class OnnxOutputContext:
|
|
19
|
+
model_output: np.ndarray
|
|
20
|
+
attention_mask: Optional[np.ndarray] = None
|
|
21
|
+
input_ids: Optional[np.ndarray] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class OnnxModel(Generic[T]):
|
|
25
|
+
@classmethod
|
|
26
|
+
def _get_worker_class(cls) -> Type["EmbeddingWorker"]:
|
|
27
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
28
|
+
|
|
29
|
+
def _post_process_onnx_output(self, output: OnnxOutputContext) -> Iterable[T]:
|
|
30
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
31
|
+
|
|
32
|
+
def __init__(self) -> None:
|
|
33
|
+
self.model = None
|
|
34
|
+
self.tokenizer = None
|
|
35
|
+
|
|
36
|
+
def _preprocess_onnx_input(
|
|
37
|
+
self, onnx_input: Dict[str, np.ndarray], **kwargs
|
|
38
|
+
) -> Dict[str, np.ndarray]:
|
|
39
|
+
"""
|
|
40
|
+
Preprocess the onnx input.
|
|
41
|
+
"""
|
|
42
|
+
return onnx_input
|
|
43
|
+
|
|
44
|
+
def load_onnx_model(
|
|
45
|
+
self,
|
|
46
|
+
model_dir: Path,
|
|
47
|
+
model_file: str,
|
|
48
|
+
threads: Optional[int],
|
|
49
|
+
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
model_path = model_dir / model_file
|
|
52
|
+
# List of Execution Providers: https://onnxruntime.ai/docs/execution-providers
|
|
53
|
+
|
|
54
|
+
onnx_providers = ["CPUExecutionProvider"] if providers is None else list(providers)
|
|
55
|
+
available_providers = ort.get_available_providers()
|
|
56
|
+
requested_provider_names = []
|
|
57
|
+
for provider in onnx_providers:
|
|
58
|
+
# check providers available
|
|
59
|
+
provider_name = provider if isinstance(provider, str) else provider[0]
|
|
60
|
+
requested_provider_names.append(provider_name)
|
|
61
|
+
if provider_name not in available_providers:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"Provider {provider_name} is not available. Available providers: {available_providers}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
so = ort.SessionOptions()
|
|
67
|
+
so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
|
68
|
+
|
|
69
|
+
if threads is not None:
|
|
70
|
+
so.intra_op_num_threads = threads
|
|
71
|
+
so.inter_op_num_threads = threads
|
|
72
|
+
|
|
73
|
+
self.model = ort.InferenceSession(
|
|
74
|
+
str(model_path), providers=onnx_providers, sess_options=so
|
|
75
|
+
)
|
|
76
|
+
if "CUDAExecutionProvider" in requested_provider_names:
|
|
77
|
+
current_providers = self.model.get_providers()
|
|
78
|
+
if "CUDAExecutionProvider" not in current_providers:
|
|
79
|
+
warnings.warn(
|
|
80
|
+
f"Attempt to set CUDAExecutionProvider failed. Current providers: {current_providers}."
|
|
81
|
+
"If you are using CUDA 12.x, install onnxruntime-gpu via "
|
|
82
|
+
"`pip install onnxruntime-gpu --extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/`",
|
|
83
|
+
RuntimeWarning,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def onnx_embed(self, *args, **kwargs) -> OnnxOutputContext:
|
|
87
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class EmbeddingWorker(Worker):
|
|
91
|
+
def init_embedding(
|
|
92
|
+
self,
|
|
93
|
+
model_name: str,
|
|
94
|
+
cache_dir: str,
|
|
95
|
+
) -> OnnxModel:
|
|
96
|
+
raise NotImplementedError()
|
|
97
|
+
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
model_name: str,
|
|
101
|
+
cache_dir: str,
|
|
102
|
+
):
|
|
103
|
+
self.model = self.init_embedding(model_name, cache_dir)
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def start(cls, model_name: str, cache_dir: str, **kwargs: Any) -> "EmbeddingWorker":
|
|
107
|
+
return cls(
|
|
108
|
+
model_name=model_name,
|
|
109
|
+
cache_dir=cache_dir,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def process(self, items: Iterable[Tuple[int, Any]]) -> Iterable[Tuple[int, Any]]:
|
|
113
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import Tuple
|
|
3
4
|
|
|
4
|
-
import numpy as np
|
|
5
5
|
from tokenizers import Tokenizer, AddedToken
|
|
6
6
|
|
|
7
|
+
from fastembed.image.transform.operators import Compose
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
def load_special_tokens(model_dir: Path) -> dict:
|
|
11
|
+
tokens_map_path = model_dir / "special_tokens_map.json"
|
|
12
|
+
if not tokens_map_path.exists():
|
|
13
|
+
raise ValueError(f"Could not find special_tokens_map.json in {model_dir}")
|
|
14
|
+
|
|
15
|
+
with open(str(tokens_map_path)) as tokens_map_file:
|
|
16
|
+
tokens_map = json.load(tokens_map_file)
|
|
17
|
+
|
|
18
|
+
return tokens_map
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_tokenizer(model_dir: Path, max_length: int = 512) -> Tuple[Tokenizer, dict]:
|
|
9
22
|
config_path = model_dir / "config.json"
|
|
10
23
|
if not config_path.exists():
|
|
11
24
|
raise ValueError(f"Could not find config.json in {model_dir}")
|
|
@@ -18,18 +31,13 @@ def load_tokenizer(model_dir: Path, max_length: int = 512) -> Tokenizer:
|
|
|
18
31
|
if not tokenizer_config_path.exists():
|
|
19
32
|
raise ValueError(f"Could not find tokenizer_config.json in {model_dir}")
|
|
20
33
|
|
|
21
|
-
tokens_map_path = model_dir / "special_tokens_map.json"
|
|
22
|
-
if not tokens_map_path.exists():
|
|
23
|
-
raise ValueError(f"Could not find special_tokens_map.json in {model_dir}")
|
|
24
|
-
|
|
25
34
|
with open(str(config_path)) as config_file:
|
|
26
35
|
config = json.load(config_file)
|
|
27
36
|
|
|
28
37
|
with open(str(tokenizer_config_path)) as tokenizer_config_file:
|
|
29
38
|
tokenizer_config = json.load(tokenizer_config_file)
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
tokens_map = json.load(tokens_map_file)
|
|
40
|
+
tokens_map = load_special_tokens(model_dir)
|
|
33
41
|
|
|
34
42
|
tokenizer = Tokenizer.from_file(str(tokenizer_path))
|
|
35
43
|
tokenizer.enable_truncation(max_length=min(tokenizer_config["model_max_length"], max_length))
|
|
@@ -43,12 +51,24 @@ def load_tokenizer(model_dir: Path, max_length: int = 512) -> Tokenizer:
|
|
|
43
51
|
elif isinstance(token, dict):
|
|
44
52
|
tokenizer.add_special_tokens([AddedToken(**token)])
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
special_token_to_id = {}
|
|
55
|
+
|
|
56
|
+
for token in tokens_map.values():
|
|
57
|
+
if isinstance(token, str):
|
|
58
|
+
special_token_to_id[token] = tokenizer.token_to_id(token)
|
|
59
|
+
elif isinstance(token, dict):
|
|
60
|
+
token_str = token.get("content", "")
|
|
61
|
+
special_token_to_id[token_str] = tokenizer.token_to_id(token_str)
|
|
62
|
+
|
|
63
|
+
return tokenizer, special_token_to_id
|
|
64
|
+
|
|
47
65
|
|
|
66
|
+
def load_preprocessor(model_dir: Path) -> Compose:
|
|
67
|
+
preprocessor_config_path = model_dir / "preprocessor_config.json"
|
|
68
|
+
if not preprocessor_config_path.exists():
|
|
69
|
+
raise ValueError(f"Could not find preprocessor_config.json in {model_dir}")
|
|
48
70
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
normalized_array = input_array / norm
|
|
54
|
-
return normalized_array
|
|
71
|
+
with open(str(preprocessor_config_path)) as preprocessor_config_file:
|
|
72
|
+
preprocessor_config = json.load(preprocessor_config_file)
|
|
73
|
+
transforms = Compose.from_config(preprocessor_config)
|
|
74
|
+
return transforms
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Union, Iterable, Tuple, Dict, Any
|
|
4
|
+
|
|
5
|
+
if sys.version_info >= (3, 10):
|
|
6
|
+
from typing import TypeAlias
|
|
7
|
+
else:
|
|
8
|
+
from typing_extensions import TypeAlias
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
PathInput: TypeAlias = Union[str, os.PathLike]
|
|
12
|
+
ImageInput: TypeAlias = Union[PathInput, Iterable[PathInput]]
|
|
13
|
+
|
|
14
|
+
OnnxProvider: TypeAlias = Union[str, Tuple[str, Dict[Any, Any]]]
|
|
@@ -4,6 +4,16 @@ from itertools import islice
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Union, Iterable, Generator, Optional
|
|
6
6
|
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def normalize(input_array, p=2, dim=1, eps=1e-12) -> np.ndarray:
|
|
11
|
+
# Calculate the Lp norm along the specified dimension
|
|
12
|
+
norm = np.linalg.norm(input_array, ord=p, axis=dim, keepdims=True)
|
|
13
|
+
norm = np.maximum(norm, eps) # Avoid division by zero
|
|
14
|
+
normalized_array = input_array / norm
|
|
15
|
+
return normalized_array
|
|
16
|
+
|
|
7
17
|
|
|
8
18
|
def iter_batch(iterable: Union[Iterable, Generator], size: int) -> Iterable:
|
|
9
19
|
"""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from typing import Any, Dict, Iterable, List, Optional, Type, Sequence
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from fastembed.common import ImageInput, OnnxProvider
|
|
6
|
+
from fastembed.image.image_embedding_base import ImageEmbeddingBase
|
|
7
|
+
from fastembed.image.onnx_embedding import OnnxImageEmbedding
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ImageEmbedding(ImageEmbeddingBase):
|
|
11
|
+
EMBEDDINGS_REGISTRY: List[Type[ImageEmbeddingBase]] = [OnnxImageEmbedding]
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def list_supported_models(cls) -> List[Dict[str, Any]]:
|
|
15
|
+
"""
|
|
16
|
+
Lists the supported models.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
List[Dict[str, Any]]: A list of dictionaries containing the model information.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
```
|
|
23
|
+
[
|
|
24
|
+
{
|
|
25
|
+
"model": "Qdrant/clip-ViT-B-32-vision",
|
|
26
|
+
"dim": 512,
|
|
27
|
+
"description": "CLIP vision encoder based on ViT-B/32",
|
|
28
|
+
"size_in_GB": 0.33,
|
|
29
|
+
"sources": {
|
|
30
|
+
"hf": "Qdrant/clip-ViT-B-32-vision",
|
|
31
|
+
},
|
|
32
|
+
"model_file": "model.onnx",
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
```
|
|
36
|
+
"""
|
|
37
|
+
result = []
|
|
38
|
+
for embedding in cls.EMBEDDINGS_REGISTRY:
|
|
39
|
+
result.extend(embedding.list_supported_models())
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
model_name: str,
|
|
45
|
+
cache_dir: Optional[str] = None,
|
|
46
|
+
threads: Optional[int] = None,
|
|
47
|
+
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
48
|
+
**kwargs,
|
|
49
|
+
):
|
|
50
|
+
super().__init__(model_name, cache_dir, threads, **kwargs)
|
|
51
|
+
|
|
52
|
+
for EMBEDDING_MODEL_TYPE in self.EMBEDDINGS_REGISTRY:
|
|
53
|
+
supported_models = EMBEDDING_MODEL_TYPE.list_supported_models()
|
|
54
|
+
if any(model_name.lower() == model["model"].lower() for model in supported_models):
|
|
55
|
+
self.model = EMBEDDING_MODEL_TYPE(
|
|
56
|
+
model_name, cache_dir, threads, providers=providers, **kwargs
|
|
57
|
+
)
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"Model {model_name} is not supported in TextEmbedding."
|
|
62
|
+
"Please check the supported models using `TextEmbedding.list_supported_models()`"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def embed(
|
|
66
|
+
self,
|
|
67
|
+
images: ImageInput,
|
|
68
|
+
batch_size: int = 16,
|
|
69
|
+
parallel: Optional[int] = None,
|
|
70
|
+
**kwargs,
|
|
71
|
+
) -> Iterable[np.ndarray]:
|
|
72
|
+
"""
|
|
73
|
+
Encode a list of documents into list of embeddings.
|
|
74
|
+
We use mean pooling with attention so that the model can handle variable-length inputs.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
images: Iterator of image paths or single image path to embed
|
|
78
|
+
batch_size: Batch size for encoding -- higher values will use more memory, but be faster
|
|
79
|
+
parallel:
|
|
80
|
+
If > 1, data-parallel encoding will be used, recommended for offline encoding of large datasets.
|
|
81
|
+
If 0, use all available cores.
|
|
82
|
+
If None, don't use data-parallel processing, use default onnxruntime threading instead.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List of embeddings, one per document
|
|
86
|
+
"""
|
|
87
|
+
yield from self.model.embed(images, batch_size, parallel, **kwargs)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Iterable, Optional
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from fastembed.common.model_management import ModelManagement
|
|
6
|
+
from fastembed.common.types import ImageInput
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ImageEmbeddingBase(ModelManagement):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
model_name: str,
|
|
13
|
+
cache_dir: Optional[str] = None,
|
|
14
|
+
threads: Optional[int] = None,
|
|
15
|
+
**kwargs,
|
|
16
|
+
):
|
|
17
|
+
self.model_name = model_name
|
|
18
|
+
self.cache_dir = cache_dir
|
|
19
|
+
self.threads = threads
|
|
20
|
+
self._local_files_only = kwargs.pop("local_files_only", False)
|
|
21
|
+
|
|
22
|
+
def embed(
|
|
23
|
+
self,
|
|
24
|
+
images: ImageInput,
|
|
25
|
+
batch_size: int = 16,
|
|
26
|
+
parallel: Optional[int] = None,
|
|
27
|
+
**kwargs,
|
|
28
|
+
) -> Iterable[np.ndarray]:
|
|
29
|
+
"""
|
|
30
|
+
Embeds a list of images into a list of embeddings.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
images - The list of image paths to preprocess and embed.
|
|
34
|
+
**kwargs: Additional keyword argument to pass to the embed method.
|
|
35
|
+
|
|
36
|
+
Yields:
|
|
37
|
+
Iterable[np.ndarray]: The embeddings.
|
|
38
|
+
"""
|
|
39
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from typing import Dict, Optional, Iterable, Type, List, Any, Sequence
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from fastembed.common.onnx_model import OnnxOutputContext
|
|
6
|
+
from fastembed.common.utils import normalize, define_cache_dir
|
|
7
|
+
from fastembed.common import ImageInput, OnnxProvider
|
|
8
|
+
from fastembed.image.image_embedding_base import ImageEmbeddingBase
|
|
9
|
+
from fastembed.image.onnx_image_model import OnnxImageModel, ImageEmbeddingWorker
|
|
10
|
+
|
|
11
|
+
supported_onnx_models = [
|
|
12
|
+
{
|
|
13
|
+
"model": "Qdrant/clip-ViT-B-32-vision",
|
|
14
|
+
"dim": 512,
|
|
15
|
+
"description": "CLIP vision encoder based on ViT-B/32",
|
|
16
|
+
"size_in_GB": 0.34,
|
|
17
|
+
"sources": {
|
|
18
|
+
"hf": "Qdrant/clip-ViT-B-32-vision",
|
|
19
|
+
},
|
|
20
|
+
"model_file": "model.onnx",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"model": "Qdrant/resnet50-onnx",
|
|
24
|
+
"dim": 2048,
|
|
25
|
+
"description": "ResNet-50 from `Deep Residual Learning for Image Recognition <https://arxiv.org/abs/1512.03385>`__.",
|
|
26
|
+
"size_in_GB": 0.1,
|
|
27
|
+
"sources": {
|
|
28
|
+
"hf": "Qdrant/resnet50-onnx",
|
|
29
|
+
},
|
|
30
|
+
"model_file": "model.onnx",
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OnnxImageEmbedding(ImageEmbeddingBase, OnnxImageModel[np.ndarray]):
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
model_name: str,
|
|
39
|
+
cache_dir: Optional[str] = None,
|
|
40
|
+
threads: Optional[int] = None,
|
|
41
|
+
providers: Optional[Sequence[OnnxProvider]] = None,
|
|
42
|
+
**kwargs,
|
|
43
|
+
):
|
|
44
|
+
"""
|
|
45
|
+
Args:
|
|
46
|
+
model_name (str): The name of the model to use.
|
|
47
|
+
cache_dir (str, optional): The path to the cache directory.
|
|
48
|
+
Can be set using the `FASTEMBED_CACHE_PATH` env variable.
|
|
49
|
+
Defaults to `fastembed_cache` in the system's temp directory.
|
|
50
|
+
threads (int, optional): The number of threads single onnxruntime session can use. Defaults to None.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
ValueError: If the model_name is not in the format <org>/<model> e.g. BAAI/bge-base-en.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
super().__init__(model_name, cache_dir, threads, **kwargs)
|
|
57
|
+
|
|
58
|
+
model_description = self._get_model_description(model_name)
|
|
59
|
+
cache_dir = define_cache_dir(cache_dir)
|
|
60
|
+
model_dir = self.download_model(
|
|
61
|
+
model_description, cache_dir, local_files_only=self._local_files_only
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self.load_onnx_model(
|
|
65
|
+
model_dir=model_dir,
|
|
66
|
+
model_file=model_description["model_file"],
|
|
67
|
+
threads=threads,
|
|
68
|
+
providers=providers,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def list_supported_models(cls) -> List[Dict[str, Any]]:
|
|
73
|
+
"""
|
|
74
|
+
Lists the supported models.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
List[Dict[str, Any]]: A list of dictionaries containing the model information.
|
|
78
|
+
"""
|
|
79
|
+
return supported_onnx_models
|
|
80
|
+
|
|
81
|
+
def embed(
|
|
82
|
+
self,
|
|
83
|
+
images: ImageInput,
|
|
84
|
+
batch_size: int = 16,
|
|
85
|
+
parallel: Optional[int] = None,
|
|
86
|
+
**kwargs,
|
|
87
|
+
) -> Iterable[np.ndarray]:
|
|
88
|
+
"""
|
|
89
|
+
Encode a list of images into list of embeddings.
|
|
90
|
+
We use mean pooling with attention so that the model can handle variable-length inputs.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
images: Iterator of image paths or single image path to embed
|
|
94
|
+
batch_size: Batch size for encoding -- higher values will use more memory, but be faster
|
|
95
|
+
parallel:
|
|
96
|
+
If > 1, data-parallel encoding will be used, recommended for offline encoding of large datasets.
|
|
97
|
+
If 0, use all available cores.
|
|
98
|
+
If None, don't use data-parallel processing, use default onnxruntime threading instead.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of embeddings, one per document
|
|
102
|
+
"""
|
|
103
|
+
yield from self._embed_images(
|
|
104
|
+
model_name=self.model_name,
|
|
105
|
+
cache_dir=str(self.cache_dir),
|
|
106
|
+
images=images,
|
|
107
|
+
batch_size=batch_size,
|
|
108
|
+
parallel=parallel,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def _get_worker_class(cls) -> Type["ImageEmbeddingWorker"]:
|
|
113
|
+
return OnnxImageEmbeddingWorker
|
|
114
|
+
|
|
115
|
+
def _preprocess_onnx_input(
|
|
116
|
+
self, onnx_input: Dict[str, np.ndarray], **kwargs
|
|
117
|
+
) -> Dict[str, np.ndarray]:
|
|
118
|
+
"""
|
|
119
|
+
Preprocess the onnx input.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
return onnx_input
|
|
123
|
+
|
|
124
|
+
def _post_process_onnx_output(self, output: OnnxOutputContext) -> Iterable[np.ndarray]:
|
|
125
|
+
return normalize(output.model_output).astype(np.float32)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class OnnxImageEmbeddingWorker(ImageEmbeddingWorker):
|
|
129
|
+
def init_embedding(
|
|
130
|
+
self,
|
|
131
|
+
model_name: str,
|
|
132
|
+
cache_dir: str,
|
|
133
|
+
) -> OnnxImageEmbedding:
|
|
134
|
+
return OnnxImageEmbedding(model_name=model_name, cache_dir=cache_dir, threads=1)
|