syntaxhacker commited on
Commit
3f3aedf
·
1 Parent(s): 7bffc25

feat: switch to ONNX runtime for embeddings

Browse files
Files changed (3) hide show
  1. app/config.py +3 -2
  2. app/pipeline.py +116 -14
  3. requirements.txt +4 -1
app/config.py CHANGED
@@ -73,7 +73,8 @@ DATASET_CONFIGS = {
73
  ),
74
  }
75
 
76
- # Default configuration for embedding model
77
  MODEL_CONFIG = {
78
- "embedding_model": "sentence-transformers/all-MiniLM-L6-v2",
 
79
  }
 
73
  ),
74
  }
75
 
76
+ # Default configuration for embedding model (ONNX)
77
  MODEL_CONFIG = {
78
+ "embedding_model": "Xenova/all-MiniLM-L6-v2", # Pre-exported ONNX model
79
+ "use_onnx": True,
80
  }
app/pipeline.py CHANGED
@@ -1,13 +1,118 @@
1
  from haystack import Document, Pipeline
2
  from haystack.document_stores.in_memory import InMemoryDocumentStore
3
- from haystack.components.embedders import SentenceTransformersTextEmbedder, SentenceTransformersDocumentEmbedder
4
  from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
5
  from haystack.components.builders import PromptBuilder
6
  from datasets import load_dataset
7
- from haystack.dataclasses import ChatMessage
8
  from typing import Optional, List, Union, Dict
 
 
 
 
9
  from .config import DatasetConfig, DATASET_CONFIGS, MODEL_CONFIG
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  class RAGPipeline:
12
  def __init__(
13
  self,
@@ -53,22 +158,19 @@ class RAGPipeline:
53
  )
54
  self.documents.append(document)
55
 
56
- # Documents loaded silently - remove verbose output
57
-
58
- # Initialize components
59
  self.document_store = InMemoryDocumentStore()
60
- self.doc_embedder = SentenceTransformersDocumentEmbedder(
61
- model=embedding_model or MODEL_CONFIG["embedding_model"],
62
- progress_bar=False
63
- )
64
- self.text_embedder = SentenceTransformersTextEmbedder(
65
- model=embedding_model or MODEL_CONFIG["embedding_model"],
66
  progress_bar=False
67
  )
68
- self.text_embedder = SentenceTransformersTextEmbedder(
69
- model=embedding_model or MODEL_CONFIG["embedding_model"],
70
- progress_bar=False
71
  )
 
72
  self.retriever = InMemoryEmbeddingRetriever(self.document_store)
73
 
74
  # Warm up the embedders
 
1
  from haystack import Document, Pipeline
2
  from haystack.document_stores.in_memory import InMemoryDocumentStore
 
3
  from haystack.components.retrievers.in_memory import InMemoryEmbeddingRetriever
4
  from haystack.components.builders import PromptBuilder
5
  from datasets import load_dataset
 
6
  from typing import Optional, List, Union, Dict
7
+ import numpy as np
8
+ import logging
9
+ import os
10
+
11
  from .config import DatasetConfig, DATASET_CONFIGS, MODEL_CONFIG
12
 
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ONNXTextEmbedder:
17
+ """ONNX-based text embedder using optimum."""
18
+
19
+ def __init__(self, model_name: str):
20
+ from optimum.onnxruntime import ORTModelForFeatureExtraction
21
+ from transformers import AutoTokenizer
22
+
23
+ logger.info(f"Loading ONNX model: {model_name}")
24
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
25
+
26
+ # Load pre-exported ONNX model (no export needed)
27
+ self.model = ORTModelForFeatureExtraction.from_pretrained(
28
+ model_name,
29
+ file_name="onnx/model.onnx",
30
+ provider="CPUExecutionProvider"
31
+ )
32
+ logger.info("ONNX model loaded successfully")
33
+
34
+ def _mean_pooling(self, model_output, attention_mask):
35
+ token_embeddings = model_output[0]
36
+ input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
37
+ return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
38
+
39
+ def run(self, text: str):
40
+ import torch
41
+
42
+ encoded = self.tokenizer(
43
+ text,
44
+ padding=True,
45
+ truncation=True,
46
+ max_length=512,
47
+ return_tensors='pt'
48
+ )
49
+
50
+ with torch.no_grad():
51
+ model_output = self.model(**encoded)
52
+
53
+ embeddings = self._mean_pooling(model_output, encoded['attention_mask'])
54
+ embedding = embeddings[0].numpy().tolist()
55
+
56
+ return {"embedding": embedding}
57
+
58
+ def warm_up(self):
59
+ pass
60
+
61
+
62
+ class ONNXDocumentEmbedder:
63
+ """ONNX-based document embedder using optimum."""
64
+
65
+ def __init__(self, model_name: str, progress_bar: bool = False):
66
+ from optimum.onnxruntime import ORTModelForFeatureExtraction
67
+ from transformers import AutoTokenizer
68
+
69
+ logger.info(f"Loading ONNX model: {model_name}")
70
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
71
+
72
+ # Load pre-exported ONNX model (no export needed)
73
+ self.model = ORTModelForFeatureExtraction.from_pretrained(
74
+ model_name,
75
+ file_name="onnx/model.onnx",
76
+ provider="CPUExecutionProvider"
77
+ )
78
+ self.progress_bar = progress_bar
79
+ logger.info("ONNX model loaded successfully")
80
+
81
+ def _mean_pooling(self, model_output, attention_mask):
82
+ token_embeddings = model_output[0]
83
+ input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
84
+ return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
85
+
86
+ def run(self, documents):
87
+ import torch
88
+ from haystack import Document
89
+
90
+ texts = [doc.content for doc in documents]
91
+
92
+ encoded = self.tokenizer(
93
+ texts,
94
+ padding=True,
95
+ truncation=True,
96
+ max_length=512,
97
+ return_tensors='pt'
98
+ )
99
+
100
+ with torch.no_grad():
101
+ model_output = self.model(**encoded)
102
+
103
+ embeddings = self._mean_pooling(model_output, encoded['attention_mask'])
104
+
105
+ docs_with_embeddings = []
106
+ for doc, embedding in zip(documents, embeddings.numpy()):
107
+ doc_with_emb = Document(content=doc.content, meta=doc.meta, embedding=embedding.tolist())
108
+ docs_with_embeddings.append(doc_with_emb)
109
+
110
+ return {"documents": docs_with_embeddings}
111
+
112
+ def warm_up(self):
113
+ pass
114
+
115
+
116
  class RAGPipeline:
117
  def __init__(
118
  self,
 
158
  )
159
  self.documents.append(document)
160
 
161
+ # Initialize components with ONNX
 
 
162
  self.document_store = InMemoryDocumentStore()
163
+
164
+ model_name = embedding_model or MODEL_CONFIG["embedding_model"]
165
+
166
+ self.doc_embedder = ONNXDocumentEmbedder(
167
+ model_name=model_name,
 
168
  progress_bar=False
169
  )
170
+ self.text_embedder = ONNXTextEmbedder(
171
+ model_name=model_name
 
172
  )
173
+
174
  self.retriever = InMemoryEmbeddingRetriever(self.document_store)
175
 
176
  # Warm up the embedders
requirements.txt CHANGED
@@ -1,6 +1,9 @@
1
  haystack-ai==2.10.3
2
  datasets==3.3.2
3
- sentence-transformers==3.4.1
 
 
 
4
  fastapi==0.115.4
5
  uvicorn==0.31.0
6
  openai==1.57.0
 
1
  haystack-ai==2.10.3
2
  datasets==3.3.2
3
+ optimum[onnxruntime]==1.23.3
4
+ onnxruntime==1.20.1
5
+ onnxscript>=0.1.0
6
+ transformers==4.46.3
7
  fastapi==0.115.4
8
  uvicorn==0.31.0
9
  openai==1.57.0