syntaxhacker commited on
Commit
2925df7
·
1 Parent(s): 3f3aedf

fix: use onnxruntime directly without torch dependency

Browse files
Files changed (2) hide show
  1. app/pipeline.py +77 -54
  2. requirements.txt +1 -2
app/pipeline.py CHANGED
@@ -1,4 +1,4 @@
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
@@ -7,86 +7,103 @@ 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(
@@ -94,23 +111,33 @@ class ONNXDocumentEmbedder:
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:
@@ -173,10 +200,6 @@ class RAGPipeline:
173
 
174
  self.retriever = InMemoryEmbeddingRetriever(self.document_store)
175
 
176
- # Warm up the embedders
177
- self.doc_embedder.warm_up()
178
- self.text_embedder.warm_up()
179
-
180
  # Initialize prompt template
181
  self.prompt_builder = PromptBuilder(template=self.config.prompt_template or """
182
  Given the following context, please answer the question.
 
1
+ from haystack import Document, Pipeline, component
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
 
7
  import numpy as np
8
  import logging
9
  import os
10
+ import onnxruntime as ort
11
+ from transformers import AutoTokenizer
12
 
13
  from .config import DatasetConfig, DATASET_CONFIGS, MODEL_CONFIG
14
 
15
  logger = logging.getLogger(__name__)
16
 
17
 
18
+ @component
19
  class ONNXTextEmbedder:
20
+ """ONNX-based text embedder using onnxruntime directly."""
21
 
22
  def __init__(self, model_name: str):
 
 
 
23
  logger.info(f"Loading ONNX model: {model_name}")
24
  self.tokenizer = AutoTokenizer.from_pretrained(model_name)
25
 
26
+ # Download and cache the ONNX model
27
+ from huggingface_hub import hf_hub_download
28
+ onnx_path = hf_hub_download(
29
+ repo_id=model_name,
30
+ filename="onnx/model.onnx"
31
+ )
32
+
33
+ # Create ONNX Runtime session
34
+ self.session = ort.InferenceSession(
35
+ onnx_path,
36
+ providers=['CPUExecutionProvider']
37
  )
38
  logger.info("ONNX model loaded successfully")
39
 
40
+ def _mean_pooling(self, token_embeddings: np.ndarray, attention_mask: np.ndarray) -> np.ndarray:
41
+ """Perform mean pooling on token embeddings using numpy."""
42
+ input_mask_expanded = np.expand_dims(attention_mask, axis=-1)
43
+ sum_embeddings = np.sum(token_embeddings * input_mask_expanded, axis=1)
44
+ sum_mask = np.clip(np.sum(input_mask_expanded, axis=1), a_min=1e-9, a_max=None)
45
+ return sum_embeddings / sum_mask
46
 
47
+ @component.output_types(embedding=List[float])
48
  def run(self, text: str):
 
 
49
  encoded = self.tokenizer(
50
  text,
51
  padding=True,
52
  truncation=True,
53
  max_length=512,
54
+ return_tensors='np'
55
  )
56
 
57
+ # Run ONNX inference
58
+ inputs = {
59
+ 'input_ids': encoded['input_ids'].astype(np.int64),
60
+ 'attention_mask': encoded['attention_mask'].astype(np.int64)
61
+ }
62
 
63
+ if 'token_type_ids' in encoded:
64
+ inputs['token_type_ids'] = encoded['token_type_ids'].astype(np.int64)
65
+
66
+ outputs = self.session.run(None, inputs)
67
+ token_embeddings = outputs[0]
68
+
69
+ embeddings = self._mean_pooling(token_embeddings, encoded['attention_mask'])
70
+ embedding = embeddings[0].tolist()
71
 
72
  return {"embedding": embedding}
 
 
 
73
 
74
 
75
+ @component
76
  class ONNXDocumentEmbedder:
77
+ """ONNX-based document embedder using onnxruntime directly."""
78
 
79
  def __init__(self, model_name: str, progress_bar: bool = False):
 
 
 
80
  logger.info(f"Loading ONNX model: {model_name}")
81
  self.tokenizer = AutoTokenizer.from_pretrained(model_name)
82
 
83
+ # Download and cache the ONNX model
84
+ from huggingface_hub import hf_hub_download
85
+ onnx_path = hf_hub_download(
86
+ repo_id=model_name,
87
+ filename="onnx/model.onnx"
88
+ )
89
+
90
+ # Create ONNX Runtime session
91
+ self.session = ort.InferenceSession(
92
+ onnx_path,
93
+ providers=['CPUExecutionProvider']
94
  )
95
  self.progress_bar = progress_bar
96
  logger.info("ONNX model loaded successfully")
97
 
98
+ def _mean_pooling(self, token_embeddings: np.ndarray, attention_mask: np.ndarray) -> np.ndarray:
99
+ """Perform mean pooling on token embeddings using numpy."""
100
+ input_mask_expanded = np.expand_dims(attention_mask, axis=-1)
101
+ sum_embeddings = np.sum(token_embeddings * input_mask_expanded, axis=1)
102
+ sum_mask = np.clip(np.sum(input_mask_expanded, axis=1), a_min=1e-9, a_max=None)
103
+ return sum_embeddings / sum_mask
104
 
105
+ @component.output_types(documents=List[Document])
106
+ def run(self, documents: List[Document]):
 
 
107
  texts = [doc.content for doc in documents]
108
 
109
  encoded = self.tokenizer(
 
111
  padding=True,
112
  truncation=True,
113
  max_length=512,
114
+ return_tensors='np'
115
  )
116
 
117
+ # Run ONNX inference
118
+ inputs = {
119
+ 'input_ids': encoded['input_ids'].astype(np.int64),
120
+ 'attention_mask': encoded['attention_mask'].astype(np.int64)
121
+ }
122
+
123
+ if 'token_type_ids' in encoded:
124
+ inputs['token_type_ids'] = encoded['token_type_ids'].astype(np.int64)
125
 
126
+ outputs = self.session.run(None, inputs)
127
+ token_embeddings = outputs[0]
128
+
129
+ embeddings = self._mean_pooling(token_embeddings, encoded['attention_mask'])
130
 
131
  docs_with_embeddings = []
132
+ for doc, embedding in zip(documents, embeddings):
133
+ doc_with_emb = Document(
134
+ content=doc.content,
135
+ meta=doc.meta,
136
+ embedding=embedding.tolist()
137
+ )
138
  docs_with_embeddings.append(doc_with_emb)
139
 
140
  return {"documents": docs_with_embeddings}
 
 
 
141
 
142
 
143
  class RAGPipeline:
 
200
 
201
  self.retriever = InMemoryEmbeddingRetriever(self.document_store)
202
 
 
 
 
 
203
  # Initialize prompt template
204
  self.prompt_builder = PromptBuilder(template=self.config.prompt_template or """
205
  Given the following context, please answer the question.
requirements.txt CHANGED
@@ -1,9 +1,8 @@
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
 
1
  haystack-ai==2.10.3
2
  datasets==3.3.2
 
3
  onnxruntime==1.20.1
 
4
  transformers==4.46.3
5
+ huggingface-hub>=0.24.0
6
  fastapi==0.115.4
7
  uvicorn==0.31.0
8
  openai==1.57.0