ConvAI-Group-60 / app.py
MythSus's picture
Update app.py
6505fc0 verified
Raw
History Blame Contribute Delete
11.4 kB
# import time
# import gradio as gr
# import torch
# from transformers import GPT2LMHeadModel, GPT2Tokenizer
# from peft import PeftModel, LoraConfig
# tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
# tokenizer.pad_token = tokenizer.eos_token
# base_model = GPT2LMHeadModel.from_pretrained("gpt2-medium")
# lora_config = LoraConfig(
# r=8,
# lora_alpha=16,
# target_modules=["c_fc", "c_proj", "c_attn"],
# lora_dropout=0.1,
# task_type="CAUSAL_LM"
# )
# finetuned_model = PeftModel.from_pretrained(base_model, "./lora_ft_weights", config=lora_config)
# finetuned_model.eval()
# # -------------------------------
# # Simulated QA Models
# # -------------------------------
# def qa_system(method, question):
# start_time = time.time()
# if not question.strip():
# return "**Error:** Please enter a question.", 0.0, "0 seconds", ""
# # Simulated response based on method
# if method == "Retrieval-Augmented Generation (RAG)":
# answer = "Using RAG: Based on retrieved financial documents, the answer is $95,000,000."
# model_name = "RAG-based Model"
# confidence = 0.92
# else:
# model_name = "GPT 2-finetuned"
# # Input guradrails
# financial_keywords = [
# 'revenue', 'profit', 'earnings', 'financial', 'income', 'balance',
# 'cash', 'debt', 'equity', 'assets', 'market', 'investment', 'sales',
# 'cost', 'margin', 'growth', 'compliance', 'risk', 'customer'
# ]
# for text in question:
# # Check for financial content
# text_lower = text.lower()
# if any(pattern in text_lower for pattern in financial_keywords):
# return "This question does not seem to be related to Finance"
# prompt = f"You are a financial assistant.\nUse the context below to answer the question.\n\nQuestion: {question}\nAnswer:"
# inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
# with torch.no_grad():
# outputs = finetuned_model.generate(
# **inputs,
# max_length=inputs['input_ids'].shape[1] + 100,
# temperature=0.7,
# do_sample=True,
# pad_token_id=tokenizer.eos_token_id,
# )
# generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
# answer = generated_text.split("Answer:")[-1].strip()
# end_time = time.time()
# response_time = round(end_time - start_time, 2)
# return (
# f"**Method:** {model_name}",
# 0.95,
# f"{response_time} seconds",
# answer
# )
# # -------------------------------
# # Gradio UI
# # -------------------------------
# with gr.Blocks(css="""
# .radio-vertical .wrap {
# flex-direction: column !important;
# }
# .radio-vertical .wrap > label {
# margin-bottom: 8px !important;
# margin-right: 0 !important;
# }
# .small-btn {
# max-width: fit-content !important;
# width: auto !important;
# }
# .small-btn button {
# width: auto !important;
# min-width: unset !important;
# padding: 8px 16px !important;
# font-size: 16px !important;
# white-space: nowrap !important;
# max-width: fit-content !important;
# }
# """) as demo:
# gr.Markdown(
# """
# # 📊 Comparative Financial QA System
# 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.
# """
# )
# # Radio buttons displayed vertically
# method = gr.Radio(
# choices=["Retrieval-Augmented Generation (RAG)", "Fine-Tuned Model"],
# label="Choose QA Method:",
# value="Fine-Tuned Model",
# interactive=True,
# elem_classes="radio-vertical"
# )
# # Question input
# question = gr.Textbox(
# label="Ask a question about Nice's 2023-2024 financials:",
# placeholder="e.g., What was the total revenue in 2023?"
# )
# # Get Answer button — auto-sized
# submit_btn = gr.Button("Get Answer", elem_classes="small-btn")
# # Output section - initially hidden
# with gr.Group(visible=False) as output_section:
# method_output = gr.Markdown()
# confidence_output = gr.Number(label="Model Confidence")
# response_time_output = gr.Textbox(label="Response Time")
# answer_output = gr.Markdown(label="Answer")
# # Button click handler
# def handle_submit(method_val, question_val):
# # Show output section and get results
# results = qa_system(method_val, question_val)
# return [gr.Group(visible=True)] + list(results)
# submit_btn.click(
# handle_submit,
# inputs=[method, question],
# outputs=[output_section, method_output, confidence_output, response_time_output, answer_output]
# )
# # -------------------------------
# # Launch for Hugging Face Spaces
# # -------------------------------
# if __name__ == "__main__":
# demo.launch()
import time, re, numpy as np, pickle, faiss
import gradio as gr
import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer
from peft import PeftModel, LoraConfig
from sentence_transformers import SentenceTransformer, CrossEncoder
from rank_bm25 import BM25Okapi
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
# -------------------------------
# Fine-Tuned GPT2 Setup
# -------------------------------
tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
tokenizer.pad_token = tokenizer.eos_token
base_model = GPT2LMHeadModel.from_pretrained("gpt2-medium")
lora_config = LoraConfig(
r=8, lora_alpha=16,
target_modules=["c_fc","c_proj","c_attn"],
lora_dropout=0.1, task_type="CAUSAL_LM"
)
finetuned_model = PeftModel.from_pretrained(base_model, "./lora_ft_weights", config=lora_config)
finetuned_model.eval()
# -------------------------------
# RAG Setup
# -------------------------------
# Load chunks + FAISS
with open("financial_chunks.pkl","rb") as f:
chunks = pickle.load(f)
dense_index = faiss.read_index("financial_index.faiss")
# Sparse BM25
tokenized_corpus = [c["text"].split(" ") for c in chunks]
bm25_index = BM25Okapi(tokenized_corpus)
# Embedding + reranker
embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
# Generator
gen_name = "google/flan-t5-large"
rag_tok = AutoTokenizer.from_pretrained(gen_name)
rag_model = AutoModelForSeq2SeqLM.from_pretrained(gen_name)
def detect_numeric_query(query: str) -> bool:
return any(k in query.lower() for k in ["revenue","income","profit","eps","earnings","assets","liabilities","cash","dividend","margin","cost","expenses","sales","percentage"])
def hybrid_retrieve(query, top_k=20, alpha=0.6):
query_embedding = embedder.encode([query], convert_to_numpy=True)
D_dense, I_dense = dense_index.search(query_embedding.astype(np.float32), top_k)
dense_results = [{'chunk': chunks[i], 'score': float(D_dense[0][j])} for j, i in enumerate(I_dense[0])]
tokenized_query = query.lower().split(" ")
sparse_scores_list = bm25_index.get_scores(tokenized_query)
sparse_indices = np.argsort(sparse_scores_list)[-top_k:][::-1]
sparse_results = [{'chunk': chunks[i], 'score': float(sparse_scores_list[i])} for i in sparse_indices]
fused = {c['chunk']['id']: 0 for c in dense_results + sparse_results}
for res in dense_results:
fused[res['chunk']['id']] += alpha * (1 - res['score']/(1+res['score']))
for res in sparse_results:
fused[res['chunk']['id']] += (1-alpha) * res['score']
candidates = [c for c in chunks if c["id"] in fused]
pairs = [(query, c["text"]) for c in candidates]
scores = reranker.predict(pairs)
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
return [c for c, s in ranked[:6]]
def rag_pipeline(query: str):
if not query.strip():
return "Empty query", 0.0, 0.0
t0 = time.time()
ctxs = hybrid_retrieve(query)
if not detect_numeric_query(query):
pairs = [(query, c["text"]) for c in ctxs]
scores = reranker.predict(pairs)
ctxs = [c for c, _ in sorted(zip(ctxs, scores), key=lambda x: x[1], reverse=True)[:1]]
context = " ".join([c["text"] for c in ctxs])
if detect_numeric_query(query):
prompt = f"Context:\n{context}\n\nQuestion: {query}\nAnswer with the exact numeric value."
else:
prompt = f"Context:\n{context}\n\nQuestion: {query}\nAnswer in 2-3 sentences."
inputs = rag_tok(prompt, return_tensors="pt", max_length=1024, truncation=True)
outputs = rag_model.generate(**inputs, max_new_tokens=120)
ans = rag_tok.decode(outputs[0], skip_special_tokens=True)
return ans.strip(), 0.9, round(time.time()-t0,2)
# -------------------------------
# Unified QA System
# -------------------------------
def qa_system(method, question):
start_time = time.time()
if method == "Retrieval-Augmented Generation (RAG)":
answer, confidence, elapsed = rag_pipeline(question)
return f"**Method:** RAG-based Model", confidence, f"{elapsed} seconds", answer
else:
# Fine-tuned GPT2
prompt = f"You are a financial assistant.\nQuestion: {question}\nAnswer:"
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
outputs = finetuned_model.generate(**inputs, max_length=inputs['input_ids'].shape[1]+100,
temperature=0.7, do_sample=True,
pad_token_id=tokenizer.eos_token_id)
answer = tokenizer.decode(outputs[0], skip_special_tokens=True).split("Answer:")[-1].strip()
if "Question:" in answer:
answer = answer.split("Question:")[0].strip()
end_time = time.time()
response_time = round(end_time - start_time, 2)
return f"**Method:** Fine-Tuned Model", 0.95, response_time, answer.strip()
# -------------------------------
# Gradio UI (same as before)
# -------------------------------
with gr.Blocks() as demo:
gr.Markdown("# 📊 Comparative Financial QA System")
method = gr.Radio(["Retrieval-Augmented Generation (RAG)", "Fine-Tuned Model"],
label="Choose QA Method:", value="Fine-Tuned Model")
question = gr.Textbox(label="Ask a question:")
submit_btn = gr.Button("Get Answer")
with gr.Group(visible=False) as output_section:
method_output = gr.Markdown()
confidence_output = gr.Number(label="Model Confidence")
response_time_output = gr.Textbox(label="Response Time")
answer_output = gr.Markdown(label="Answer")
def handle_submit(method_val, question_val):
results = qa_system(method_val, question_val)
return [gr.Group(visible=True)] + list(results)
submit_btn.click(handle_submit, [method, question],
[output_section, method_output, confidence_output, response_time_output, answer_output])
if __name__ == "__main__":
demo.launch()