mirror of
https://github.com/qdrant/fastembed.git
synced 2026-09-22 05:57:51 -05:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e21de21d6 | ||
|
|
4bc5fbedf9 | ||
|
|
3d90072e8a | ||
|
|
5ea3bbcc84 | ||
|
|
1c016a2a3f | ||
|
|
4037e14f3b | ||
|
|
758d33984d | ||
|
|
5c46b17a24 | ||
|
|
f63333d620 | ||
|
|
13b7d6d7ef | ||
|
|
c212c1fe41 | ||
|
|
b82e4d05f9 | ||
|
|
a761dcf657 | ||
|
|
b8d30b1cb6 | ||
|
|
a18f735983 | ||
|
|
4c5001595c |
File diff suppressed because one or more lines are too long
@@ -68,7 +68,15 @@ class OnnxModel(Generic[T]):
|
||||
if device_id is None:
|
||||
onnx_providers = ["CUDAExecutionProvider"]
|
||||
else:
|
||||
onnx_providers = [("CUDAExecutionProvider", {"device_id": device_id})]
|
||||
# kSameAsRequested: Allocates only the requested memory, avoiding over-allocation.
|
||||
# more precise than 'kNextPowerOfTwo', which grows memory aggressively.
|
||||
# source: https://onnxruntime.ai/docs/get-started/with-c.html#features:~:text=Memory%20arena%20shrinkage:
|
||||
onnx_providers = [
|
||||
(
|
||||
"CUDAExecutionProvider",
|
||||
{"device_id": device_id, "arena_extend_strategy": "kSameAsRequested"},
|
||||
)
|
||||
]
|
||||
else:
|
||||
onnx_providers = ["CPUExecutionProvider"]
|
||||
|
||||
@@ -132,5 +140,7 @@ class EmbeddingWorker(Worker, Generic[T]):
|
||||
def start(cls, model_name: str, cache_dir: str, **kwargs: Any) -> "EmbeddingWorker[T]":
|
||||
return cls(model_name=model_name, cache_dir=cache_dir, **kwargs)
|
||||
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, Any]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, Any]]:
|
||||
raise NotImplementedError("Subclasses must implement this method")
|
||||
|
||||
@@ -5,12 +5,12 @@ import tempfile
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
from itertools import islice
|
||||
from typing import Iterable, Optional, TypeVar
|
||||
from typing import Iterable, Optional, TypeVar, Sequence
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from fastembed.common.types import NumpyArray
|
||||
from fastembed.common.types import NumpyArray, OnnxProvider
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@@ -67,3 +67,18 @@ def get_all_punctuation() -> set[str]:
|
||||
|
||||
def remove_non_alphanumeric(text: str) -> str:
|
||||
return re.sub(r"[^\w\s]", " ", text, flags=re.UNICODE)
|
||||
|
||||
|
||||
def is_cuda_enabled(cuda: bool, providers: Optional[Sequence[OnnxProvider]]) -> bool:
|
||||
"""
|
||||
Check if CUDA is enabled based on the `cuda` and `providers` parameters
|
||||
"""
|
||||
if cuda:
|
||||
return True
|
||||
if not providers:
|
||||
return False
|
||||
if isinstance(providers, str):
|
||||
return "CUDAExecutionProvider" in providers
|
||||
return isinstance(providers, (list, tuple)) and any(
|
||||
isinstance(p, str) and "CUDAExecutionProvider" in p for p in providers
|
||||
)
|
||||
|
||||
@@ -6,13 +6,14 @@ from typing import Any, Iterable, Optional, Sequence, Type, Union
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import onnxruntime as ort
|
||||
|
||||
from fastembed.image.transform.operators import Compose
|
||||
from fastembed.common.types import NumpyArray
|
||||
from fastembed.common import ImageInput, OnnxProvider
|
||||
from fastembed.common.onnx_model import EmbeddingWorker, OnnxModel, OnnxOutputContext, T
|
||||
from fastembed.common.preprocessor_utils import load_preprocessor
|
||||
from fastembed.common.utils import iter_batch
|
||||
from fastembed.common.utils import iter_batch, is_cuda_enabled
|
||||
from fastembed.parallel_processor import ParallelWorkerPool
|
||||
|
||||
# Holds type of the embedding result
|
||||
@@ -74,7 +75,21 @@ class OnnxImageModel(OnnxModel[T]):
|
||||
encoded = np.array(self.processor(image_files))
|
||||
onnx_input = self._build_onnx_input(encoded)
|
||||
onnx_input = self._preprocess_onnx_input(onnx_input)
|
||||
model_output = self.model.run(None, onnx_input) # type: ignore[union-attr]
|
||||
|
||||
run_options = ort.RunOptions()
|
||||
providers = kwargs.get("providers", None)
|
||||
cuda = kwargs.get("cuda", False)
|
||||
if is_cuda_enabled(cuda, providers):
|
||||
device_id = kwargs.get("device_id", None)
|
||||
device_id = str(device_id if isinstance(device_id, int) else 0)
|
||||
# enables memory arena shrinkage, freeing unused memory after each Run() cycle.
|
||||
# helps prevent excessive memory retention, especially for dynamic workloads.
|
||||
# source: https://onnxruntime.ai/docs/get-started/with-c.html#features:~:text=Memory%20arena%20shrinkage:
|
||||
run_options.add_run_config_entry(
|
||||
"memory.enable_memory_arena_shrinkage", f"gpu:{device_id}"
|
||||
)
|
||||
|
||||
model_output = self.model.run(None, onnx_input, run_options) # type: ignore[union-attr]
|
||||
embeddings = model_output[0].reshape(len(images), -1)
|
||||
return OnnxOutputContext(model_output=embeddings)
|
||||
|
||||
@@ -104,7 +119,9 @@ class OnnxImageModel(OnnxModel[T]):
|
||||
self.load_onnx_model()
|
||||
|
||||
for batch in iter_batch(images, batch_size):
|
||||
yield from self._post_process_onnx_output(self.onnx_embed(batch))
|
||||
yield from self._post_process_onnx_output(
|
||||
self.onnx_embed(batch, cuda=cuda, providers=providers)
|
||||
)
|
||||
else:
|
||||
if parallel == 0:
|
||||
parallel = os.cpu_count()
|
||||
@@ -129,7 +146,9 @@ class OnnxImageModel(OnnxModel[T]):
|
||||
|
||||
|
||||
class ImageEmbeddingWorker(EmbeddingWorker[T]):
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, Any]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, Any]]:
|
||||
for idx, batch in items:
|
||||
embeddings = self.model.onnx_embed(batch)
|
||||
embeddings = self.model.onnx_embed(batch, **kwargs)
|
||||
yield idx, embeddings
|
||||
|
||||
@@ -6,13 +6,14 @@ from typing import Any, Iterable, Optional, Sequence, Type, Union
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import onnxruntime as ort
|
||||
from tokenizers import Encoding, Tokenizer
|
||||
|
||||
from fastembed.common import OnnxProvider, ImageInput
|
||||
from fastembed.common.onnx_model import EmbeddingWorker, OnnxModel, OnnxOutputContext, T
|
||||
from fastembed.common.preprocessor_utils import load_tokenizer, load_preprocessor
|
||||
from fastembed.common.types import NumpyArray
|
||||
from fastembed.common.utils import iter_batch
|
||||
from fastembed.common.utils import iter_batch, is_cuda_enabled
|
||||
from fastembed.image.transform.operators import Compose
|
||||
from fastembed.parallel_processor import ParallelWorkerPool
|
||||
|
||||
@@ -103,7 +104,21 @@ class OnnxMultimodalModel(OnnxModel[T]):
|
||||
)
|
||||
|
||||
onnx_input = self._preprocess_onnx_text_input(onnx_input, **kwargs)
|
||||
model_output = self.model.run(self.ONNX_OUTPUT_NAMES, onnx_input) # type: ignore[union-attr]
|
||||
|
||||
run_options = ort.RunOptions()
|
||||
providers = kwargs.get("providers", None)
|
||||
cuda = kwargs.get("cuda", False)
|
||||
if is_cuda_enabled(cuda, providers):
|
||||
device_id = kwargs.get("device_id", None)
|
||||
device_id = str(device_id if isinstance(device_id, int) else 0)
|
||||
# enables memory arena shrinkage, freeing unused memory after each Run() cycle.
|
||||
# helps prevent excessive memory retention, especially for dynamic workloads.
|
||||
# source: https://onnxruntime.ai/docs/get-started/with-c.html#features:~:text=Memory%20arena%20shrinkage:
|
||||
run_options.add_run_config_entry(
|
||||
"memory.enable_memory_arena_shrinkage", f"gpu:{device_id}"
|
||||
)
|
||||
|
||||
model_output = self.model.run(self.ONNX_OUTPUT_NAMES, onnx_input, run_options) # type: ignore[union-attr]
|
||||
return OnnxOutputContext(
|
||||
model_output=model_output[0],
|
||||
attention_mask=onnx_input.get("attention_mask", attention_mask),
|
||||
@@ -136,7 +151,9 @@ class OnnxMultimodalModel(OnnxModel[T]):
|
||||
if not hasattr(self, "model") or self.model is None:
|
||||
self.load_onnx_model()
|
||||
for batch in iter_batch(documents, batch_size):
|
||||
yield from self._post_process_onnx_text_output(self.onnx_embed_text(batch))
|
||||
yield from self._post_process_onnx_text_output(
|
||||
self.onnx_embed_text(batch, cuda=cuda, providers=providers)
|
||||
)
|
||||
else:
|
||||
if parallel == 0:
|
||||
parallel = os.cpu_count()
|
||||
@@ -169,7 +186,21 @@ class OnnxMultimodalModel(OnnxModel[T]):
|
||||
encoded = np.array(self.processor(image_files))
|
||||
onnx_input = {"pixel_values": encoded}
|
||||
onnx_input = self._preprocess_onnx_image_input(onnx_input, **kwargs)
|
||||
model_output = self.model.run(None, onnx_input) # type: ignore[union-attr]
|
||||
|
||||
run_options = ort.RunOptions()
|
||||
providers = kwargs.get("providers", None)
|
||||
cuda = kwargs.get("cuda", False)
|
||||
if is_cuda_enabled(cuda, providers):
|
||||
device_id = kwargs.get("device_id", None)
|
||||
device_id = str(device_id if isinstance(device_id, int) else 0)
|
||||
# enables memory arena shrinkage, freeing unused memory after each Run() cycle.
|
||||
# helps prevent excessive memory retention, especially for dynamic workloads.
|
||||
# source: https://onnxruntime.ai/docs/get-started/with-c.html#features:~:text=Memory%20arena%20shrinkage:
|
||||
run_options.add_run_config_entry(
|
||||
"memory.enable_memory_arena_shrinkage", f"gpu:{device_id}"
|
||||
)
|
||||
|
||||
model_output = self.model.run(None, onnx_input, run_options) # type: ignore[union-attr]
|
||||
embeddings = model_output[0].reshape(len(images), -1)
|
||||
return OnnxOutputContext(model_output=embeddings)
|
||||
|
||||
@@ -199,7 +230,9 @@ class OnnxMultimodalModel(OnnxModel[T]):
|
||||
self.load_onnx_model()
|
||||
|
||||
for batch in iter_batch(images, batch_size):
|
||||
yield from self._post_process_onnx_image_output(self.onnx_embed_image(batch))
|
||||
yield from self._post_process_onnx_image_output(
|
||||
self.onnx_embed_image(batch, cuda=cuda, providers=providers)
|
||||
)
|
||||
else:
|
||||
if parallel == 0:
|
||||
parallel = os.cpu_count()
|
||||
@@ -241,9 +274,11 @@ class TextEmbeddingWorker(EmbeddingWorker[T]):
|
||||
) -> OnnxMultimodalModel:
|
||||
raise NotImplementedError()
|
||||
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, Any]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, Any]]:
|
||||
for idx, batch in items:
|
||||
onnx_output = self.model.onnx_embed_text(batch)
|
||||
onnx_output = self.model.onnx_embed_text(batch, **kwargs)
|
||||
yield idx, onnx_output
|
||||
|
||||
|
||||
@@ -265,7 +300,9 @@ class ImageEmbeddingWorker(EmbeddingWorker[T]):
|
||||
) -> OnnxMultimodalModel:
|
||||
raise NotImplementedError()
|
||||
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, Any]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, Any]]:
|
||||
for idx, batch in items:
|
||||
embeddings = self.model.onnx_embed_image(batch)
|
||||
embeddings = self.model.onnx_embed_image(batch, **kwargs)
|
||||
yield idx, embeddings
|
||||
|
||||
@@ -28,7 +28,9 @@ class Worker:
|
||||
def start(cls, *args: Any, **kwargs: Any) -> "Worker":
|
||||
raise NotImplementedError()
|
||||
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, Any]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, Any]]:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -63,7 +65,7 @@ def _worker(
|
||||
break
|
||||
yield item
|
||||
|
||||
for processed_item in worker.process(input_queue_iterable()):
|
||||
for processed_item in worker.process(input_queue_iterable(), **kwargs):
|
||||
output_queue.put(processed_item)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logging.exception(e)
|
||||
|
||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
||||
from typing import Any, Iterable, Optional, Sequence, Type
|
||||
|
||||
import numpy as np
|
||||
import onnxruntime as ort
|
||||
from tokenizers import Encoding
|
||||
|
||||
from fastembed.common.onnx_model import (
|
||||
@@ -14,7 +15,7 @@ from fastembed.common.onnx_model import (
|
||||
)
|
||||
from fastembed.common.types import NumpyArray
|
||||
from fastembed.common.preprocessor_utils import load_tokenizer
|
||||
from fastembed.common.utils import iter_batch
|
||||
from fastembed.common.utils import iter_batch, is_cuda_enabled
|
||||
from fastembed.parallel_processor import ParallelWorkerPool
|
||||
|
||||
|
||||
@@ -71,7 +72,21 @@ class OnnxCrossEncoderModel(OnnxModel[float]):
|
||||
tokenized_input = self.tokenize(pairs, **kwargs)
|
||||
inputs = self._build_onnx_input(tokenized_input)
|
||||
onnx_input = self._preprocess_onnx_input(inputs, **kwargs)
|
||||
outputs = self.model.run(self.ONNX_OUTPUT_NAMES, onnx_input) # type: ignore[union-attr]
|
||||
|
||||
run_options = ort.RunOptions()
|
||||
providers = kwargs.get("providers", None)
|
||||
cuda = kwargs.get("cuda", False)
|
||||
if is_cuda_enabled(cuda, providers):
|
||||
device_id = kwargs.get("device_id", None)
|
||||
device_id = str(device_id if isinstance(device_id, int) else 0)
|
||||
# Enables memory arena shrinkage, freeing unused memory after each Run() cycle.
|
||||
# Helps prevent excessive memory retention, especially for dynamic workloads.
|
||||
# Source: https://onnxruntime.ai/docs/get-started/with-c.html#features:~:text=Memory%20arena%20shrinkage:
|
||||
run_options.add_run_config_entry(
|
||||
"memory.enable_memory_arena_shrinkage", f"gpu:{device_id}"
|
||||
)
|
||||
|
||||
outputs = self.model.run(self.ONNX_OUTPUT_NAMES, onnx_input, run_options) # type: ignore[union-attr]
|
||||
relevant_output = outputs[0]
|
||||
scores: NumpyArray = relevant_output[:, 0]
|
||||
return OnnxOutputContext(model_output=scores)
|
||||
@@ -110,7 +125,9 @@ class OnnxCrossEncoderModel(OnnxModel[float]):
|
||||
if not hasattr(self, "model") or self.model is None:
|
||||
self.load_onnx_model()
|
||||
for batch in iter_batch(pairs, batch_size):
|
||||
yield from self._post_process_onnx_output(self.onnx_embed_pairs(batch, **kwargs))
|
||||
yield from self._post_process_onnx_output(
|
||||
self.onnx_embed_pairs(batch, cuda=cuda, providers=providers, **kwargs)
|
||||
)
|
||||
else:
|
||||
if parallel == 0:
|
||||
parallel = os.cpu_count()
|
||||
@@ -163,7 +180,9 @@ class TextRerankerWorker(EmbeddingWorker[float]):
|
||||
) -> OnnxCrossEncoderModel:
|
||||
raise NotImplementedError()
|
||||
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, Any]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, Any]]:
|
||||
for idx, batch in items:
|
||||
onnx_output = self.model.onnx_embed_pairs(batch)
|
||||
onnx_output = self.model.onnx_embed_pairs(batch, **kwargs)
|
||||
yield idx, onnx_output
|
||||
|
||||
@@ -344,7 +344,7 @@ class Bm25Worker(Worker):
|
||||
return cls(model_name=model_name, cache_dir=cache_dir, **kwargs)
|
||||
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]]
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, list[SparseEmbedding]]]:
|
||||
for idx, batch in items:
|
||||
onnx_output = self.model.raw_embed(batch)
|
||||
|
||||
@@ -4,13 +4,14 @@ from pathlib import Path
|
||||
from typing import Any, Iterable, Optional, Sequence, Type, Union
|
||||
|
||||
import numpy as np
|
||||
import onnxruntime as ort
|
||||
from numpy.typing import NDArray
|
||||
from tokenizers import Encoding, Tokenizer
|
||||
|
||||
from fastembed.common.types import NumpyArray, OnnxProvider
|
||||
from fastembed.common.onnx_model import EmbeddingWorker, OnnxModel, OnnxOutputContext, T
|
||||
from fastembed.common.preprocessor_utils import load_tokenizer
|
||||
from fastembed.common.utils import iter_batch
|
||||
from fastembed.common.utils import iter_batch, is_cuda_enabled
|
||||
from fastembed.parallel_processor import ParallelWorkerPool
|
||||
|
||||
|
||||
@@ -82,7 +83,21 @@ class OnnxTextModel(OnnxModel[T]):
|
||||
)
|
||||
onnx_input = self._preprocess_onnx_input(onnx_input, **kwargs)
|
||||
|
||||
model_output = self.model.run(self.ONNX_OUTPUT_NAMES, onnx_input) # type: ignore[union-attr]
|
||||
run_options = ort.RunOptions()
|
||||
providers = kwargs.get("providers", None)
|
||||
cuda = kwargs.get("cuda", False)
|
||||
|
||||
if is_cuda_enabled(cuda, providers):
|
||||
device_id = kwargs.get("device_id", None)
|
||||
device_id = str(device_id if isinstance(device_id, int) else 0)
|
||||
# enables memory arena shrinkage, freeing unused memory after each Run() cycle.
|
||||
# helps prevent excessive memory retention, especially for dynamic workloads.
|
||||
# source: https://onnxruntime.ai/docs/get-started/with-c.html#features:~:text=Memory%20arena%20shrinkage:
|
||||
run_options.add_run_config_entry(
|
||||
"memory.enable_memory_arena_shrinkage", f"gpu:{device_id}"
|
||||
)
|
||||
|
||||
model_output = self.model.run(self.ONNX_OUTPUT_NAMES, onnx_input, run_options) # type: ignore[union-attr]
|
||||
return OnnxOutputContext(
|
||||
model_output=model_output[0],
|
||||
attention_mask=onnx_input.get("attention_mask", attention_mask),
|
||||
@@ -115,7 +130,9 @@ class OnnxTextModel(OnnxModel[T]):
|
||||
if not hasattr(self, "model") or self.model is None:
|
||||
self.load_onnx_model()
|
||||
for batch in iter_batch(documents, batch_size):
|
||||
yield from self._post_process_onnx_output(self.onnx_embed(batch))
|
||||
yield from self._post_process_onnx_output(
|
||||
self.onnx_embed(batch, cuda=cuda, providers=providers)
|
||||
)
|
||||
else:
|
||||
if parallel == 0:
|
||||
parallel = os.cpu_count()
|
||||
@@ -140,7 +157,9 @@ class OnnxTextModel(OnnxModel[T]):
|
||||
|
||||
|
||||
class TextEmbeddingWorker(EmbeddingWorker[T]):
|
||||
def process(self, items: Iterable[tuple[int, Any]]) -> Iterable[tuple[int, OnnxOutputContext]]:
|
||||
def process(
|
||||
self, items: Iterable[tuple[int, Any]], **kwargs: Any
|
||||
) -> Iterable[tuple[int, OnnxOutputContext]]:
|
||||
for idx, batch in items:
|
||||
onnx_output = self.model.onnx_embed(batch)
|
||||
onnx_output = self.model.onnx_embed(batch, **kwargs)
|
||||
yield idx, onnx_output
|
||||
|
||||
Reference in New Issue
Block a user