KYBits commited on
Commit
8575778
·
verified ·
1 Parent(s): d27e82b

Update app.py

Browse files

added rag part

Files changed (1) hide show
  1. app.py +256 -114
app.py CHANGED
@@ -1,152 +1,294 @@
1
- import time
2
- import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import torch
5
  from transformers import GPT2LMHeadModel, GPT2Tokenizer
6
  from peft import PeftModel, LoraConfig
 
 
 
7
 
8
-
 
 
9
  tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
10
  tokenizer.pad_token = tokenizer.eos_token
11
  base_model = GPT2LMHeadModel.from_pretrained("gpt2-medium")
12
 
13
  lora_config = LoraConfig(
14
- r=8,
15
- lora_alpha=16,
16
- target_modules=["c_fc", "c_proj", "c_attn"],
17
- lora_dropout=0.1,
18
- task_type="CAUSAL_LM"
19
  )
20
-
21
  finetuned_model = PeftModel.from_pretrained(base_model, "./lora_ft_weights", config=lora_config)
22
  finetuned_model.eval()
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  # -------------------------------
27
- # Simulated QA Models
28
  # -------------------------------
29
  def qa_system(method, question):
30
- start_time = time.time()
31
-
32
- if not question.strip():
33
- return "**Error:** Please enter a question.", 0.0, "0 seconds", ""
34
-
35
- # Simulated response based on method
36
  if method == "Retrieval-Augmented Generation (RAG)":
37
- answer = "Using RAG: Based on retrieved financial documents, the answer is $95,000,000."
38
- model_name = "RAG-based Model"
39
- confidence = 0.92
40
  else:
41
- model_name = "GPT 2-finetuned"
42
- # Input guradrails
43
- financial_keywords = [
44
- 'revenue', 'profit', 'earnings', 'financial', 'income', 'balance',
45
- 'cash', 'debt', 'equity', 'assets', 'market', 'investment', 'sales',
46
- 'cost', 'margin', 'growth', 'compliance', 'risk', 'customer'
47
- ]
48
- for text in question:
49
- # Check for financial content
50
- text_lower = text.lower()
51
- if any(pattern in text_lower for pattern in financial_keywords):
52
- return "This question does not seem to be related to Finance"
53
-
54
- prompt = f"You are a financial assistant.\nUse the context below to answer the question.\n\nQuestion: {question}\nAnswer:"
55
-
56
  inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
57
-
58
  with torch.no_grad():
59
- outputs = finetuned_model.generate(
60
- **inputs,
61
- max_length=inputs['input_ids'].shape[1] + 100,
62
- temperature=0.7,
63
- do_sample=True,
64
- pad_token_id=tokenizer.eos_token_id,
65
- )
66
-
67
- generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
68
- answer = generated_text.split("Answer:")[-1].strip()
69
-
70
- end_time = time.time()
71
- response_time = round(end_time - start_time, 2)
72
-
73
- return (
74
- f"**Method:** {model_name}",
75
- 0.95,
76
- f"{response_time} seconds",
77
- answer
78
- )
79
 
80
  # -------------------------------
81
- # Gradio UI
82
  # -------------------------------
83
- with gr.Blocks(css="""
84
- .radio-vertical .wrap {
85
- flex-direction: column !important;
86
- }
87
- .radio-vertical .wrap > label {
88
- margin-bottom: 8px !important;
89
- margin-right: 0 !important;
90
- }
91
- .small-btn {
92
- max-width: fit-content !important;
93
- width: auto !important;
94
- }
95
- .small-btn button {
96
- width: auto !important;
97
- min-width: unset !important;
98
- padding: 8px 16px !important;
99
- font-size: 16px !important;
100
- white-space: nowrap !important;
101
- max-width: fit-content !important;
102
- }
103
- """) as demo:
104
- gr.Markdown(
105
- """
106
- # 📊 Comparative Financial QA System
107
- An implementation comparing **Retrieval-Augmented Generation (RAG)** and a **Fine-Tuned on LoRA and Replay-Based Learning** GPT 2 model for answering questions on financial reports.
108
- """
109
- )
110
-
111
- # Radio buttons displayed vertically
112
- method = gr.Radio(
113
- choices=["Retrieval-Augmented Generation (RAG)", "Fine-Tuned Model"],
114
- label="Choose QA Method:",
115
- value="Fine-Tuned Model",
116
- interactive=True,
117
- elem_classes="radio-vertical"
118
- )
119
-
120
- # Question input
121
- question = gr.Textbox(
122
- label="Ask a question about Nice's 2023-2024 financials:",
123
- placeholder="e.g., What was the total revenue in 2023?"
124
- )
125
-
126
- # Get Answer button — auto-sized
127
- submit_btn = gr.Button("Get Answer", elem_classes="small-btn")
128
-
129
- # Output section - initially hidden
130
  with gr.Group(visible=False) as output_section:
131
  method_output = gr.Markdown()
132
  confidence_output = gr.Number(label="Model Confidence")
133
  response_time_output = gr.Textbox(label="Response Time")
134
  answer_output = gr.Markdown(label="Answer")
135
 
136
- # Button click handler
137
  def handle_submit(method_val, question_val):
138
- # Show output section and get results
139
  results = qa_system(method_val, question_val)
140
  return [gr.Group(visible=True)] + list(results)
141
-
142
- submit_btn.click(
143
- handle_submit,
144
- inputs=[method, question],
145
- outputs=[output_section, method_output, confidence_output, response_time_output, answer_output]
146
- )
147
 
148
- # -------------------------------
149
- # Launch for Hugging Face Spaces
150
- # -------------------------------
151
  if __name__ == "__main__":
152
  demo.launch()
 
1
+ # import time
2
+ # import gradio as gr
3
+
4
+ # import torch
5
+ # from transformers import GPT2LMHeadModel, GPT2Tokenizer
6
+ # from peft import PeftModel, LoraConfig
7
+
8
+
9
+ # tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
10
+ # tokenizer.pad_token = tokenizer.eos_token
11
+ # base_model = GPT2LMHeadModel.from_pretrained("gpt2-medium")
12
+
13
+ # lora_config = LoraConfig(
14
+ # r=8,
15
+ # lora_alpha=16,
16
+ # target_modules=["c_fc", "c_proj", "c_attn"],
17
+ # lora_dropout=0.1,
18
+ # task_type="CAUSAL_LM"
19
+ # )
20
+
21
+ # finetuned_model = PeftModel.from_pretrained(base_model, "./lora_ft_weights", config=lora_config)
22
+ # finetuned_model.eval()
23
+
24
+
25
+
26
+ # # -------------------------------
27
+ # # Simulated QA Models
28
+ # # -------------------------------
29
+ # def qa_system(method, question):
30
+ # start_time = time.time()
31
+
32
+ # if not question.strip():
33
+ # return "**Error:** Please enter a question.", 0.0, "0 seconds", ""
34
+
35
+ # # Simulated response based on method
36
+ # if method == "Retrieval-Augmented Generation (RAG)":
37
+ # answer = "Using RAG: Based on retrieved financial documents, the answer is $95,000,000."
38
+ # model_name = "RAG-based Model"
39
+ # confidence = 0.92
40
+ # else:
41
+ # model_name = "GPT 2-finetuned"
42
+ # # Input guradrails
43
+ # financial_keywords = [
44
+ # 'revenue', 'profit', 'earnings', 'financial', 'income', 'balance',
45
+ # 'cash', 'debt', 'equity', 'assets', 'market', 'investment', 'sales',
46
+ # 'cost', 'margin', 'growth', 'compliance', 'risk', 'customer'
47
+ # ]
48
+ # for text in question:
49
+ # # Check for financial content
50
+ # text_lower = text.lower()
51
+ # if any(pattern in text_lower for pattern in financial_keywords):
52
+ # return "This question does not seem to be related to Finance"
53
+
54
+ # prompt = f"You are a financial assistant.\nUse the context below to answer the question.\n\nQuestion: {question}\nAnswer:"
55
+
56
+ # inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
57
+
58
+ # with torch.no_grad():
59
+ # outputs = finetuned_model.generate(
60
+ # **inputs,
61
+ # max_length=inputs['input_ids'].shape[1] + 100,
62
+ # temperature=0.7,
63
+ # do_sample=True,
64
+ # pad_token_id=tokenizer.eos_token_id,
65
+ # )
66
+
67
+ # generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
68
+ # answer = generated_text.split("Answer:")[-1].strip()
69
+
70
+ # end_time = time.time()
71
+ # response_time = round(end_time - start_time, 2)
72
 
73
+ # return (
74
+ # f"**Method:** {model_name}",
75
+ # 0.95,
76
+ # f"{response_time} seconds",
77
+ # answer
78
+ # )
79
+
80
+ # # -------------------------------
81
+ # # Gradio UI
82
+ # # -------------------------------
83
+ # with gr.Blocks(css="""
84
+ # .radio-vertical .wrap {
85
+ # flex-direction: column !important;
86
+ # }
87
+ # .radio-vertical .wrap > label {
88
+ # margin-bottom: 8px !important;
89
+ # margin-right: 0 !important;
90
+ # }
91
+ # .small-btn {
92
+ # max-width: fit-content !important;
93
+ # width: auto !important;
94
+ # }
95
+ # .small-btn button {
96
+ # width: auto !important;
97
+ # min-width: unset !important;
98
+ # padding: 8px 16px !important;
99
+ # font-size: 16px !important;
100
+ # white-space: nowrap !important;
101
+ # max-width: fit-content !important;
102
+ # }
103
+ # """) as demo:
104
+ # gr.Markdown(
105
+ # """
106
+ # # 📊 Comparative Financial QA System
107
+ # An implementation comparing **Retrieval-Augmented Generation (RAG)** and a **Fine-Tuned on LoRA and Replay-Based Learning** GPT 2 model for answering questions on financial reports.
108
+ # """
109
+ # )
110
+
111
+ # # Radio buttons displayed vertically
112
+ # method = gr.Radio(
113
+ # choices=["Retrieval-Augmented Generation (RAG)", "Fine-Tuned Model"],
114
+ # label="Choose QA Method:",
115
+ # value="Fine-Tuned Model",
116
+ # interactive=True,
117
+ # elem_classes="radio-vertical"
118
+ # )
119
+
120
+ # # Question input
121
+ # question = gr.Textbox(
122
+ # label="Ask a question about Nice's 2023-2024 financials:",
123
+ # placeholder="e.g., What was the total revenue in 2023?"
124
+ # )
125
+
126
+ # # Get Answer button — auto-sized
127
+ # submit_btn = gr.Button("Get Answer", elem_classes="small-btn")
128
+
129
+ # # Output section - initially hidden
130
+ # with gr.Group(visible=False) as output_section:
131
+ # method_output = gr.Markdown()
132
+ # confidence_output = gr.Number(label="Model Confidence")
133
+ # response_time_output = gr.Textbox(label="Response Time")
134
+ # answer_output = gr.Markdown(label="Answer")
135
+
136
+ # # Button click handler
137
+ # def handle_submit(method_val, question_val):
138
+ # # Show output section and get results
139
+ # results = qa_system(method_val, question_val)
140
+ # return [gr.Group(visible=True)] + list(results)
141
+
142
+ # submit_btn.click(
143
+ # handle_submit,
144
+ # inputs=[method, question],
145
+ # outputs=[output_section, method_output, confidence_output, response_time_output, answer_output]
146
+ # )
147
+
148
+ # # -------------------------------
149
+ # # Launch for Hugging Face Spaces
150
+ # # -------------------------------
151
+ # if __name__ == "__main__":
152
+ # demo.launch()
153
+
154
+
155
+
156
+ import time, re, numpy as np, pickle, faiss
157
+ import gradio as gr
158
  import torch
159
  from transformers import GPT2LMHeadModel, GPT2Tokenizer
160
  from peft import PeftModel, LoraConfig
161
+ from sentence_transformers import SentenceTransformer, CrossEncoder
162
+ from rank_bm25 import BM25Okapi
163
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
164
 
165
+ # -------------------------------
166
+ # Fine-Tuned GPT2 Setup
167
+ # -------------------------------
168
  tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
169
  tokenizer.pad_token = tokenizer.eos_token
170
  base_model = GPT2LMHeadModel.from_pretrained("gpt2-medium")
171
 
172
  lora_config = LoraConfig(
173
+ r=8, lora_alpha=16,
174
+ target_modules=["c_fc","c_proj","c_attn"],
175
+ lora_dropout=0.1, task_type="CAUSAL_LM"
 
 
176
  )
 
177
  finetuned_model = PeftModel.from_pretrained(base_model, "./lora_ft_weights", config=lora_config)
178
  finetuned_model.eval()
179
 
180
+ # -------------------------------
181
+ # RAG Setup
182
+ # -------------------------------
183
+ # Load chunks + FAISS
184
+ with open("financial_chunks.pkl","rb") as f:
185
+ chunks = pickle.load(f)
186
+ dense_index = faiss.read_index("financial_index.faiss")
187
+
188
+ # Sparse BM25
189
+ tokenized_corpus = [c["text"].split(" ") for c in chunks]
190
+ bm25_index = BM25Okapi(tokenized_corpus)
191
+
192
+ # Embedding + reranker
193
+ embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
194
+ reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
195
+
196
+ # Generator
197
+ gen_name = "google/flan-t5-large"
198
+ rag_tok = AutoTokenizer.from_pretrained(gen_name)
199
+ rag_model = AutoModelForSeq2SeqLM.from_pretrained(gen_name)
200
+
201
+ def detect_numeric_query(query: str) -> bool:
202
+ return any(k in query.lower() for k in ["revenue","income","profit","eps","earnings","assets","liabilities","cash","dividend","margin","cost","expenses","sales","percentage"])
203
+
204
+ def hybrid_retrieve(query, top_k=20, alpha=0.6):
205
+ query_embedding = embedder.encode([query], convert_to_numpy=True)
206
+ D_dense, I_dense = dense_index.search(query_embedding.astype(np.float32), top_k)
207
+ dense_results = [{'chunk': chunks[i], 'score': float(D_dense[0][j])} for j, i in enumerate(I_dense[0])]
208
 
209
+ tokenized_query = query.lower().split(" ")
210
+ sparse_scores_list = bm25_index.get_scores(tokenized_query)
211
+ sparse_indices = np.argsort(sparse_scores_list)[-top_k:][::-1]
212
+ sparse_results = [{'chunk': chunks[i], 'score': float(sparse_scores_list[i])} for i in sparse_indices]
213
+
214
+ fused = {c['chunk']['id']: 0 for c in dense_results + sparse_results}
215
+ for res in dense_results:
216
+ fused[res['chunk']['id']] += alpha * (1 - res['score']/(1+res['score']))
217
+ for res in sparse_results:
218
+ fused[res['chunk']['id']] += (1-alpha) * res['score']
219
+
220
+ candidates = [c for c in chunks if c["id"] in fused]
221
+ pairs = [(query, c["text"]) for c in candidates]
222
+ scores = reranker.predict(pairs)
223
+ ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
224
+ return [c for c, s in ranked[:6]]
225
+
226
+ def rag_pipeline(query: str):
227
+ if not query.strip():
228
+ return "Empty query", 0.0, 0.0
229
+
230
+ t0 = time.time()
231
+ ctxs = hybrid_retrieve(query)
232
+
233
+ if not detect_numeric_query(query):
234
+ pairs = [(query, c["text"]) for c in ctxs]
235
+ scores = reranker.predict(pairs)
236
+ ctxs = [c for c, _ in sorted(zip(ctxs, scores), key=lambda x: x[1], reverse=True)[:1]]
237
+
238
+ context = " ".join([c["text"] for c in ctxs])
239
+
240
+ if detect_numeric_query(query):
241
+ prompt = f"Context:\n{context}\n\nQuestion: {query}\nAnswer with the exact numeric value."
242
+ else:
243
+ prompt = f"Context:\n{context}\n\nQuestion: {query}\nAnswer in 2-3 sentences."
244
+
245
+ inputs = rag_tok(prompt, return_tensors="pt", max_length=1024, truncation=True)
246
+ outputs = rag_model.generate(**inputs, max_new_tokens=120)
247
+ ans = rag_tok.decode(outputs[0], skip_special_tokens=True)
248
+
249
+ return ans.strip(), 0.9, round(time.time()-t0,2)
250
 
251
  # -------------------------------
252
+ # Unified QA System
253
  # -------------------------------
254
  def qa_system(method, question):
 
 
 
 
 
 
255
  if method == "Retrieval-Augmented Generation (RAG)":
256
+ answer, confidence, elapsed = rag_pipeline(question)
257
+ return f"**Method:** RAG-based Model", confidence, f"{elapsed} seconds", answer
 
258
  else:
259
+ # Fine-tuned GPT2
260
+ prompt = f"You are a financial assistant.\nQuestion: {question}\nAnswer:"
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
 
262
  with torch.no_grad():
263
+ outputs = finetuned_model.generate(**inputs, max_length=inputs['input_ids'].shape[1]+100,
264
+ temperature=0.7, do_sample=True,
265
+ pad_token_id=tokenizer.eos_token_id)
266
+ answer = tokenizer.decode(outputs[0], skip_special_tokens=True).split("Answer:")[-1].strip()
267
+ return f"**Method:** Fine-Tuned Model", 0.95, "N/A", answer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
  # -------------------------------
270
+ # Gradio UI (same as before)
271
  # -------------------------------
272
+ with gr.Blocks() as demo:
273
+ gr.Markdown("# 📊 Comparative Financial QA System")
274
+
275
+ method = gr.Radio(["Retrieval-Augmented Generation (RAG)", "Fine-Tuned Model"],
276
+ label="Choose QA Method:", value="Fine-Tuned Model")
277
+ question = gr.Textbox(label="Ask a question:")
278
+ submit_btn = gr.Button("Get Answer")
279
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  with gr.Group(visible=False) as output_section:
281
  method_output = gr.Markdown()
282
  confidence_output = gr.Number(label="Model Confidence")
283
  response_time_output = gr.Textbox(label="Response Time")
284
  answer_output = gr.Markdown(label="Answer")
285
 
 
286
  def handle_submit(method_val, question_val):
 
287
  results = qa_system(method_val, question_val)
288
  return [gr.Group(visible=True)] + list(results)
 
 
 
 
 
 
289
 
290
+ submit_btn.click(handle_submit, [method, question],
291
+ [output_section, method_output, confidence_output, response_time_output, answer_output])
292
+
293
  if __name__ == "__main__":
294
  demo.launch()