Jesudian commited on
Commit
ceea46c
·
verified ·
1 Parent(s): 4028c58

Upload 6 files

Browse files

initiated the project

Files changed (7) hide show
  1. .gitattributes +1 -0
  2. README.md +85 -7
  3. app.py +56 -0
  4. clinical_guidelines.pdf +3 -0
  5. rag_pipeline.py +78 -0
  6. requirements.txt +9 -0
  7. utils.py +72 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ clinical_guidelines.pdf filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,91 @@
1
  ---
2
- title: HealthcareGuidelinesRAGChatbot
3
- emoji: 💻
4
- colorFrom: red
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: 5.32.0
8
  app_file: app.py
9
  pinned: false
10
- short_description: This application provides a Retrieval-Augmented Generation
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Healthcare Guidelines RAG Chatbot
3
+ emoji: 📄
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: 5.29.0
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
  ---
12
 
13
+
14
+ # Healthcare Guidelines RAG Chatbot
15
+
16
+ This application provides a Retrieval-Augmented Generation (RAG) system for healthcare clinical guidelines. It uses FAISS for efficient similarity search and a Hugging Face model for generating responses.
17
+
18
+ ## Features
19
+
20
+ - PDF document processing and chunking
21
+ - FAISS-based semantic search
22
+ - Response generation using FLAN-T5
23
+ - Gradio web interface
24
+ - Strict grounding in provided guidelines
25
+ - Safety measures and disclaimers
26
+
27
+ ## Setup
28
+
29
+ ### Option 1: Local Setup
30
+ 2. Install dependencies:
31
+ ```bash
32
+ pip install -r requirements.txt
33
+ ```
34
+
35
+ 3. Place your clinical guidelines PDF in the directory:
36
+ ```
37
+ # Copy your PDF to clinical_guidelines.pdf
38
+ ```
39
+
40
+ 1. Build the Docker image:
41
+ ```bash
42
+ docker build -t healthcare-rag-chatbot .
43
+ ```
44
+
45
+ 2. Run the container:
46
+ ```bash
47
+ docker run -p 7860:7860 -v $(pwd)/data:/app/data healthcare-rag-chatbot
48
+ ```
49
+
50
+ The `-v` flag mounts your local `data` directory to the container, allowing you to easily update the clinical guidelines PDF without rebuilding the image.
51
+
52
+ ## Usage
53
+
54
+ 1. Run the application:
55
+ ```bash
56
+ # If using local setup:
57
+ python app.py
58
+
59
+ # If using Docker:
60
+ # The application will start automatically when running the container
61
+ ```
62
+
63
+ 2. Open your browser and navigate to the provided local URL (typically http://127.0.0.1:7860)
64
+
65
+ 3. Enter your question about clinical guidelines in the input box
66
+
67
+ ## Important Notes
68
+
69
+ - The system will only provide information that is explicitly stated in the provided guidelines
70
+ - All responses include a medical disclaimer
71
+ - The system will respond with "This information is not available in the current guidelines" when uncertain
72
+
73
+ ## Deployment
74
+
75
+ To deploy on Hugging Face Spaces:
76
+
77
+ 1. Create a new Space on Hugging Face
78
+ 2. Connect your GitHub repository
79
+ 3. Select Gradio as the SDK
80
+ 4. The app will automatically deploy
81
+
82
+ ## Safety Measures
83
+
84
+ - All responses are strictly grounded in the provided guidelines
85
+ - A medical disclaimer is included with every response
86
+ - The system explicitly states when information is not available
87
+ - No medical advice is provided without proper context
88
+
89
+ ## License
90
+
91
+ This project is for educational purposes only. Always consult healthcare professionals for medical advice.
app.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from rag_pipeline import HealthcareRAG
3
+ import os
4
+
5
+ # Initialize RAG system
6
+ rag = HealthcareRAG()
7
+
8
+ def process_query(query: str) -> tuple:
9
+ """Process user query and return response with retrieved chunks."""
10
+ result = rag.query(query)
11
+
12
+ # Format retrieved chunks for display
13
+ chunks_text = "\n\n---\n\n".join(result["retrieved_chunks"])
14
+
15
+ return result["response"], chunks_text
16
+
17
+ # Create Gradio interface
18
+ with gr.Blocks(title="Healthcare Guidelines Assistant") as demo:
19
+ gr.Markdown("""
20
+ # Healthcare Guidelines Assistant
21
+
22
+ This assistant provides information based on clinical guidelines.
23
+ Please note that this is for educational purposes only and not medical advice.
24
+
25
+ **DISCLAIMER**: This information is for educational purposes only and not medical advice.
26
+ Always consult with healthcare professionals for medical decisions.
27
+ """)
28
+
29
+ with gr.Row():
30
+ with gr.Column():
31
+ query_input = gr.Textbox(
32
+ label="Your Question",
33
+ placeholder="Enter your question about clinical guidelines...",
34
+ lines=3
35
+ )
36
+ submit_btn = gr.Button("Submit")
37
+
38
+ with gr.Row():
39
+ with gr.Column():
40
+ response_output = gr.Textbox(
41
+ label="Response",
42
+ lines=5
43
+ )
44
+ chunks_output = gr.Textbox(
45
+ label="Retrieved Guidelines",
46
+ lines=10
47
+ )
48
+
49
+ submit_btn.click(
50
+ fn=process_query,
51
+ inputs=query_input,
52
+ outputs=[response_output, chunks_output]
53
+ )
54
+
55
+ if __name__ == "__main__":
56
+ demo.launch()
clinical_guidelines.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:74e988ffe0603263ce131d043a187302d5845fbc4ff30aa74f9190ce99ff5016
3
+ size 4917213
rag_pipeline.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
2
+ import torch
3
+ from utils import DocumentProcessor
4
+ import os
5
+ from typing import List, Dict
6
+
7
+ class HealthcareRAG:
8
+ def __init__(self,
9
+ model_name: str = "google/flan-t5-base",
10
+ index_path: str = "faiss_index.bin",
11
+ pdf_path: str = "clinical_guidelines.pdf"):
12
+
13
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
14
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
15
+ self.model = AutoModelForSeq2SeqLM.from_pretrained(model_name).to(self.device)
16
+
17
+ self.doc_processor = DocumentProcessor()
18
+
19
+ # Initialize or load FAISS index
20
+ if os.path.exists(index_path):
21
+ self.index = self.doc_processor.load_index(index_path)
22
+ # Load chunks from a saved file (you might want to save/load chunks separately)
23
+ self.chunks = [] # Load your chunks here
24
+ else:
25
+ self.chunks, self.index = self.doc_processor.process_document(pdf_path, index_path)
26
+
27
+ def generate_response(self, query: str, retrieved_chunks: List[str]) -> str:
28
+ """Generate response using the LLM."""
29
+ if not retrieved_chunks:
30
+ return "This information is not available in the current guidelines."
31
+
32
+ # Prepare context and prompt
33
+ context = "\n".join(retrieved_chunks)
34
+ prompt = f"""Based on the following clinical guidelines, answer the question.
35
+ If the information is not explicitly stated in the guidelines, respond with "This information is not available in the current guidelines."
36
+
37
+ Guidelines:
38
+ {context}
39
+
40
+ Question: {query}
41
+
42
+ Answer:"""
43
+
44
+ # Generate response
45
+ inputs = self.tokenizer(prompt, return_tensors="pt", max_length=1024, truncation=True).to(self.device)
46
+ outputs = self.model.generate(
47
+ **inputs,
48
+ max_length=200,
49
+ num_beams=4,
50
+ temperature=0.7,
51
+ top_p=0.9,
52
+ do_sample=True
53
+ )
54
+
55
+ response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
56
+
57
+ # Add disclaimer
58
+ if response and response != "This information is not available in the current guidelines.":
59
+ response += "\n\nDISCLAIMER: This information is for educational purposes only and not medical advice."
60
+
61
+ return response
62
+
63
+ def query(self, user_query: str) -> Dict[str, str]:
64
+ """Process user query and return response with retrieved chunks."""
65
+ # Retrieve relevant chunks
66
+ retrieved_chunks = self.doc_processor.retrieve_chunks(
67
+ user_query,
68
+ self.index,
69
+ self.chunks
70
+ )
71
+
72
+ # Generate response
73
+ response = self.generate_response(user_query, retrieved_chunks)
74
+
75
+ return {
76
+ "response": response,
77
+ "retrieved_chunks": retrieved_chunks
78
+ }
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio==4.19.2
2
+ sentence-transformers==2.5.1
3
+ faiss-cpu==1.7.4
4
+ PyMuPDF==1.23.26
5
+ transformers==4.38.2
6
+ torch==2.2.1
7
+ numpy==1.26.4
8
+ pandas==2.2.1
9
+ python-dotenv==1.0.1
utils.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fitz # PyMuPDF
2
+ import numpy as np
3
+ from sentence_transformers import SentenceTransformer
4
+ import faiss
5
+ import os
6
+ from typing import List, Tuple
7
+
8
+ class DocumentProcessor:
9
+ def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
10
+ self.model = SentenceTransformer(model_name)
11
+ self.chunk_size = 500 # tokens
12
+ self.chunk_overlap = 50 # tokens
13
+
14
+ def extract_text_from_pdf(self, pdf_path: str) -> str:
15
+ """Extract text from PDF file."""
16
+ doc = fitz.open(pdf_path)
17
+ text = ""
18
+ for page in doc:
19
+ text += page.get_text()
20
+ return text
21
+
22
+ def chunk_text(self, text: str) -> List[str]:
23
+ """Split text into overlapping chunks."""
24
+ words = text.split()
25
+ chunks = []
26
+
27
+ for i in range(0, len(words), self.chunk_size - self.chunk_overlap):
28
+ chunk = " ".join(words[i:i + self.chunk_size])
29
+ chunks.append(chunk)
30
+
31
+ return chunks
32
+
33
+ def create_embeddings(self, chunks: List[str]) -> np.ndarray:
34
+ """Create embeddings for text chunks."""
35
+ return self.model.encode(chunks)
36
+
37
+ def build_faiss_index(self, embeddings: np.ndarray) -> faiss.Index:
38
+ """Build and return a FAISS index."""
39
+ dimension = embeddings.shape[1]
40
+ index = faiss.IndexFlatL2(dimension)
41
+ index.add(embeddings.astype('float32'))
42
+ return index
43
+
44
+ def save_index(self, index: faiss.Index, path: str):
45
+ """Save FAISS index to disk."""
46
+ faiss.write_index(index, path)
47
+
48
+ def load_index(self, path: str) -> faiss.Index:
49
+ """Load FAISS index from disk."""
50
+ return faiss.read_index(path)
51
+
52
+ def process_document(self, pdf_path: str, index_path: str) -> Tuple[List[str], faiss.Index]:
53
+ """Process document and create FAISS index."""
54
+ # Extract and chunk text
55
+ text = self.extract_text_from_pdf(pdf_path)
56
+ chunks = self.chunk_text(text)
57
+
58
+ # Create embeddings and index
59
+ embeddings = self.create_embeddings(chunks)
60
+ index = self.build_faiss_index(embeddings)
61
+
62
+ # Save index
63
+ self.save_index(index, index_path)
64
+
65
+ return chunks, index
66
+
67
+ def retrieve_chunks(self, query: str, index: faiss.Index, chunks: List[str], k: int = 5) -> List[str]:
68
+ """Retrieve most relevant chunks for a query."""
69
+ query_embedding = self.model.encode([query])
70
+ distances, indices = index.search(query_embedding.astype('float32'), k)
71
+
72
+ return [chunks[i] for i in indices[0]]