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.
Files changed (41) hide show
  1. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/PKG-INFO +18 -9
  2. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/README.md +9 -4
  3. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/__init__.py +9 -1
  4. fastembed_gpu-0.3.0/fastembed/common/__init__.py +3 -0
  5. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/common/model_management.py +1 -0
  6. fastembed_gpu-0.3.0/fastembed/common/onnx_model.py +113 -0
  7. fastembed_gpu-0.2.7/fastembed/common/models.py → fastembed_gpu-0.3.0/fastembed/common/preprocessor_utils.py +35 -15
  8. fastembed_gpu-0.3.0/fastembed/common/types.py +14 -0
  9. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/common/utils.py +10 -0
  10. fastembed_gpu-0.3.0/fastembed/image/__init__.py +4 -0
  11. fastembed_gpu-0.3.0/fastembed/image/image_embedding.py +87 -0
  12. fastembed_gpu-0.3.0/fastembed/image/image_embedding_base.py +39 -0
  13. fastembed_gpu-0.3.0/fastembed/image/onnx_embedding.py +134 -0
  14. fastembed_gpu-0.3.0/fastembed/image/onnx_image_model.py +107 -0
  15. fastembed_gpu-0.3.0/fastembed/image/transform/functional.py +125 -0
  16. fastembed_gpu-0.3.0/fastembed/image/transform/operators.py +168 -0
  17. fastembed_gpu-0.3.0/fastembed/late_interaction/__init__.py +4 -0
  18. fastembed_gpu-0.3.0/fastembed/late_interaction/colbert.py +196 -0
  19. fastembed_gpu-0.3.0/fastembed/late_interaction/late_interaction_embedding_base.py +60 -0
  20. fastembed_gpu-0.3.0/fastembed/late_interaction/late_interaction_text_embedding.py +104 -0
  21. fastembed_gpu-0.3.0/fastembed/sparse/bm42.py +281 -0
  22. fastembed_gpu-0.3.0/fastembed/sparse/sparse_embedding_base.py +81 -0
  23. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/sparse/sparse_text_embedding.py +14 -0
  24. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/sparse/splade_pp.py +10 -9
  25. fastembed_gpu-0.3.0/fastembed/text/clip_embedding.py +49 -0
  26. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/e5_onnx_embedding.py +2 -2
  27. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/jina_onnx_embedding.py +8 -7
  28. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/onnx_embedding.py +14 -13
  29. fastembed_gpu-0.3.0/fastembed/text/onnx_text_model.py +123 -0
  30. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/text_embedding.py +2 -0
  31. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/pyproject.toml +9 -5
  32. fastembed_gpu-0.2.7/fastembed/common/__init__.py +0 -3
  33. fastembed_gpu-0.2.7/fastembed/common/onnx_model.py +0 -165
  34. fastembed_gpu-0.2.7/fastembed/image/__init__.py +0 -0
  35. fastembed_gpu-0.2.7/fastembed/sparse/sparse_embedding_base.py +0 -44
  36. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/LICENSE +0 -0
  37. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/embedding.py +0 -0
  38. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/parallel_processor.py +0 -0
  39. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/sparse/__init__.py +0 -0
  40. {fastembed_gpu-0.2.7 → fastembed_gpu-0.3.0}/fastembed/text/__init__.py +0 -0
  41. {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.2.7
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: NirantK
9
- Author-email: nirant.bits@gmail.com
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: huggingface-hub (>=0.20,<0.21)
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: tokenizers (>=0.15,<0.16)
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. It requires installation of the `fastembed-gpu` package.
82
- Make sure not to have the `fastembed` package installed, as it might interfere with the `fastembed-gpu` package.
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(model_name="BAAI/bge-small-en-v1.5", providers=["CUDAExecutionProvider"])
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. It requires installation of the `fastembed-gpu` package.
54
- Make sure not to have the `fastembed` package installed, as it might interfere with the `fastembed-gpu` package.
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(model_name="BAAI/bge-small-en-v1.5", providers=["CUDAExecutionProvider"])
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__ = ["TextEmbedding", "SparseTextEmbedding", "SparseEmbedding"]
14
+ __all__ = [
15
+ "TextEmbedding",
16
+ "SparseTextEmbedding",
17
+ "SparseEmbedding",
18
+ "ImageEmbedding",
19
+ "LateInteractionTextEmbedding",
20
+ ]
@@ -0,0 +1,3 @@
1
+ from fastembed.common.types import OnnxProvider, ImageInput, PathInput
2
+
3
+ __all__ = ["OnnxProvider", "ImageInput", "PathInput"]
@@ -108,6 +108,7 @@ class ModelManagement:
108
108
  "tokenizer.json",
109
109
  "tokenizer_config.json",
110
110
  "special_tokens_map.json",
111
+ "preprocessor_config.json",
111
112
  ]
112
113
  if extra_patterns is not None:
113
114
  allow_patterns.extend(extra_patterns)
@@ -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
- def load_tokenizer(model_dir: Path, max_length: int = 512) -> Tokenizer:
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
- with open(str(tokens_map_path)) as tokens_map_file:
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
- return tokenizer
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
- def normalize(input_array, p=2, dim=1, eps=1e-12) -> np.ndarray:
50
- # Calculate the Lp norm along the specified dimension
51
- norm = np.linalg.norm(input_array, ord=p, axis=dim, keepdims=True)
52
- norm = np.maximum(norm, eps) # Avoid division by zero
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,4 @@
1
+ from fastembed.image.image_embedding import ImageEmbedding
2
+
3
+
4
+ __all__ = ["ImageEmbedding"]
@@ -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)