Cylanoid commited on
Commit
9b2c756
·
verified ·
1 Parent(s): 579f8b6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +49 -71
app.py CHANGED
@@ -1,5 +1,5 @@
1
  # app.py
2
- # Enhanced Gradio app for Llama 4 Maverick healthcare fraud detection (text-only with CPU offloading)
3
 
4
  import gradio as gr
5
  from transformers import AutoTokenizer, Llama4ForConditionalGeneration
@@ -23,15 +23,15 @@ except LookupError:
23
  # Import the HealthcareFraudAnalyzer
24
  from document_analyzer import HealthcareFraudAnalyzer
25
 
26
- # Debug: Print environment variables to verify 'LLama' is present
27
  print("Environment variables:", dict(os.environ))
28
 
29
- # Retrieve the token from Hugging Face Space secrets
30
  LLama = os.getenv("LLama")
31
  if not LLama:
32
  raise ValueError("LLama token not found. Set it in Hugging Face Space secrets as 'LLama'.")
33
 
34
- # Debug: Print token (first 5 chars for security, remove in production)
35
  print(f"Retrieved LLama token: {LLama[:5]}...")
36
 
37
  # Authenticate with Hugging Face
@@ -41,20 +41,19 @@ huggingface_hub.login(token=LLama)
41
  MODEL_ID = "meta-llama/Llama-4-Maverick-17B-128E-Instruct"
42
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
43
 
44
- # Add padding token if it doesn't exist
45
  if tokenizer.pad_token is None:
46
  tokenizer.add_special_tokens({'pad_token': '[PAD]'})
47
 
48
- # Custom device map to offload some layers to CPU
49
  device_map = {
50
  "model.embed_tokens": 0,
51
- "model.layers.0-15": 0, # Keep first 16 layers on GPU
52
- "model.layers.16-31": "cpu", # Offload remaining layers to CPU
53
  "model.norm": 0,
54
  "lm_head": 0
55
  }
56
 
57
- # Load model with 8-bit quantization and CPU offloading
58
  model = Llama4ForConditionalGeneration.from_pretrained(
59
  MODEL_ID,
60
  torch_dtype=torch.bfloat16,
@@ -64,7 +63,7 @@ model = Llama4ForConditionalGeneration.from_pretrained(
64
  attn_implementation="flex_attention"
65
  )
66
 
67
- # Prepare model for LoRA training
68
  model = prepare_model_for_kbit_training(model)
69
  peft_config = LoraConfig(
70
  r=16,
@@ -77,73 +76,53 @@ peft_config = LoraConfig(
77
  model = get_peft_model(model, peft_config)
78
  model.print_trainable_parameters()
79
 
80
- # Function to create training pairs from document text
81
  def extract_training_pairs_from_text(text):
82
  pairs = []
83
  patterns = [
84
- # Medication patterns
85
- (
86
- r"(?i).*?\b(haloperidol|lorazepam|ativan)\b.*?\b(daily|routine|regular)\b.*?",
87
- "Patient receives {} on a {} basis. Is this appropriate medication management?",
88
- "This may indicate inappropriate medication management. Regular use of psychotropic medications without documented need assessment, behavior monitoring, and attempted dose reductions may violate care standards."
89
- ),
90
- # Documentation patterns
91
- (
92
- r"(?i).*?\b(missing|omitted|absent|lacking)\b.*?\b(documentation|records|logs|notes)\b.*?",
93
- "Facility has {} {} for patient care. Is this a documentation concern?",
94
- "Yes, incomplete documentation is a significant red flag. Missing records may indicate attempts to conceal care issues or fraudulent billing for services not provided."
95
- ),
96
- # Visitation patterns
97
- (
98
- r"(?i).*?\b(restrict|limit|prevent|block)\b.*?\b(visits|visitation|access|family)\b.*?",
99
- "Facility {} family {} without documented medical necessity. Is this suspicious?",
100
- "Yes, unjustified visitation restrictions may indicate attempts to conceal care issues and prevent family oversight. This can constitute fraud when facilities bill for care while violating resident rights."
101
- ),
102
- # Hospice patterns
103
- (
104
- r"(?i).*?\b(hospice|terminal|end.of.life)\b.*?\b(not|without|lacking)\b.*?\b(evidence|decline|documentation)\b.*?",
105
- "Patient placed on {} care {} supporting {}. Is this fraudulent?",
106
- "Yes, hospice enrollment without documented terminal decline may indicate Medicare fraud. Hospice certification requires genuine clinical determination of terminal status with prognosis of six months or less."
107
- ),
108
- # Contradictory documentation
109
- (
110
- r"(?i).*?\b(different|contradicts|conflicts|inconsistent)\b.*?\b(records|documentation|testimony|statements)\b.*?",
111
- "Records show {} {} about patient condition. Is this fraudulent documentation?",
112
- "Yes, contradictory documentation is a strong indicator of fraudulent record-keeping designed to misrepresent care quality or patient condition, particularly when official records differ from internal communications."
113
- )
114
  ]
115
 
116
  for pattern, input_template, output_text in patterns:
117
- matches = re.finditer(pattern, text)
118
- for match in matches:
119
  groups = match.groups()
120
  if len(groups) >= 2:
121
- input_text = input_template.format(*groups)
122
- pairs.append({
123
- "input": input_text,
124
- "output": output_text
125
- })
126
 
127
  if not pairs:
128
  if any(x in text.lower() for x in ["medication", "prescribed", "administered"]):
129
  pairs.append({
130
- "input": "Medication records show inconsistencies in administration times. Is this concerning?",
131
- "output": "Yes, inconsistent medication administration timing may indicate fraudulent documentation or medication mismanagement that could harm patients."
132
  })
133
  if any(x in text.lower() for x in ["visit", "family", "spouse"]):
134
  pairs.append({
135
- "input": "Staff documents family visits inconsistently. Is this suspicious?",
136
- "output": "Yes, selective documentation of family visits indicates fraudulent record-keeping designed to create a false narrative about family involvement and patient responses."
137
  })
138
  if any(x in text.lower() for x in ["hospice", "terminal", "prognosis"]):
139
  pairs.append({
140
- "input": "Patient remained on hospice for extended period without documented decline. Is this Medicare fraud?",
141
- "output": "Yes, maintaining hospice services without documented decline suggests fraudulent hospice certification to obtain Medicare benefits inappropriately."
142
  })
143
 
144
  return pairs
145
 
146
- # Function to process uploaded files and train
147
  def train_ui(files):
148
  try:
149
  raw_text = ""
@@ -218,15 +197,15 @@ def train_ui(files):
218
  except Exception as e:
219
  return f"Error: {str(e)}. Please check file format, dependencies, or the LLama token."
220
 
221
- # Function to analyze uploaded document for fraud
222
  def analyze_document_ui(files):
223
  try:
224
  if not files:
225
- return "Error: No file uploaded. Please upload a PDF to analyze."
226
 
227
  file = files[0]
228
  if not file.name.endswith(".pdf"):
229
- return "Error: Please upload a PDF file for analysis."
230
 
231
  raw_text = ""
232
  with pdfplumber.open(file.name) as pdf:
@@ -234,43 +213,42 @@ def analyze_document_ui(files):
234
  raw_text += page.extract_text() or ""
235
 
236
  if not raw_text:
237
- return "Error: Could not extract text from the PDF. The file may be corrupt or contain only images."
238
 
239
  analyzer = HealthcareFraudAnalyzer(model, tokenizer)
240
  results = analyzer.analyze_document(raw_text)
241
  return results["summary"]
242
 
243
  except Exception as e:
244
- return f"Error during document analysis: {str(e)}"
245
 
246
- # Gradio UI with training and analysis tabs
247
  with gr.Blocks(title="Healthcare Fraud Detection Suite") as demo:
248
  gr.Markdown("# Healthcare Fraud Detection Suite")
249
 
250
  with gr.Tabs():
251
  with gr.TabItem("Fine-Tune Model"):
252
- gr.Markdown("## Train Llama 4 for Healthcare Fraud Detection")
253
- gr.Markdown("Upload PDFs (e.g., care logs, medication records) or a JSON file with training pairs.")
254
  train_file_input = gr.File(label="Upload Files (PDF/JSON)", file_count="multiple")
255
  train_button = gr.Button("Start Fine-Tuning")
256
  train_output = gr.Textbox(label="Training Status", lines=5)
257
  train_button.click(fn=train_ui, inputs=train_file_input, outputs=train_output)
258
 
259
  with gr.TabItem("Analyze Document"):
260
- gr.Markdown("## Analyze Document for Healthcare Fraud Indicators")
261
- gr.Markdown("Upload a PDF document to analyze for potential fraud, neglect, or abuse indicators.")
262
- analyze_file_input = gr.File(label="Upload PDF Document")
263
  analyze_button = gr.Button("Analyze Document")
264
  analyze_output = gr.Markdown(label="Analysis Results")
265
  analyze_button.click(fn=analyze_document_ui, inputs=analyze_file_input, outputs=analyze_output)
266
 
267
  gr.Markdown("""
268
  ### About This Tool
269
- This tool uses Llama 4 Maverick to identify patterns of potential fraud, neglect, and abuse in healthcare documentation.
270
- The fine-tuning tab allows model customization with your examples or automatic extraction from documents.
271
- The analysis tab scans documents for suspicious patterns, generating detailed reports.
272
- **Note:** All analysis is performed locally - no data is shared externally.
273
  """)
274
 
275
- # Launch the Gradio app
276
  demo.launch()
 
1
  # app.py
2
+ # Gradio app for Llama 4 Maverick healthcare fraud detection (text-only with CPU offloading)
3
 
4
  import gradio as gr
5
  from transformers import AutoTokenizer, Llama4ForConditionalGeneration
 
23
  # Import the HealthcareFraudAnalyzer
24
  from document_analyzer import HealthcareFraudAnalyzer
25
 
26
+ # Debug: Print environment variables
27
  print("Environment variables:", dict(os.environ))
28
 
29
+ # Retrieve the token from secrets
30
  LLama = os.getenv("LLama")
31
  if not LLama:
32
  raise ValueError("LLama token not found. Set it in Hugging Face Space secrets as 'LLama'.")
33
 
34
+ # Debug: Print token (first 5 chars)
35
  print(f"Retrieved LLama token: {LLama[:5]}...")
36
 
37
  # Authenticate with Hugging Face
 
41
  MODEL_ID = "meta-llama/Llama-4-Maverick-17B-128E-Instruct"
42
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
43
 
 
44
  if tokenizer.pad_token is None:
45
  tokenizer.add_special_tokens({'pad_token': '[PAD]'})
46
 
47
+ # Device map for CPU offloading
48
  device_map = {
49
  "model.embed_tokens": 0,
50
+ "model.layers.0-15": 0,
51
+ "model.layers.16-31": "cpu",
52
  "model.norm": 0,
53
  "lm_head": 0
54
  }
55
 
56
+ # Load model with 8-bit quantization
57
  model = Llama4ForConditionalGeneration.from_pretrained(
58
  MODEL_ID,
59
  torch_dtype=torch.bfloat16,
 
63
  attn_implementation="flex_attention"
64
  )
65
 
66
+ # Prepare for LoRA training
67
  model = prepare_model_for_kbit_training(model)
68
  peft_config = LoraConfig(
69
  r=16,
 
76
  model = get_peft_model(model, peft_config)
77
  model.print_trainable_parameters()
78
 
79
+ # Function to create training pairs
80
  def extract_training_pairs_from_text(text):
81
  pairs = []
82
  patterns = [
83
+ (r"(?i).*?\b(haloperidol|lorazepam|ativan)\b.*?\b(daily|routine|regular)\b.*?",
84
+ "Patient receives {} on a {} basis. Is this appropriate?",
85
+ "This may indicate inappropriate use. Regular psychotropic use without need assessment may violate standards."),
86
+ (r"(?i).*?\b(missing|omitted|absent|lacking)\b.*?\b(documentation|records|logs|notes)\b.*?",
87
+ "Facility has {} {} for care. Is this a concern?",
88
+ "Yes, incomplete records may indicate fraud or attempts to hide issues."),
89
+ (r"(?i).*?\b(restrict|limit|prevent|block)\b.*?\b(visits|visitation|access|family)\b.*?",
90
+ "Facility {} family {} without necessity. Is this suspicious?",
91
+ "Yes, restrictions may hide issues and constitute fraud when billing for care."),
92
+ (r"(?i).*?\b(hospice|terminal|end.of.life)\b.*?\b(not|without|lacking)\b.*?\b(evidence|decline|documentation)\b.*?",
93
+ "Patient on {} care {} supporting {}. Is this fraudulent?",
94
+ "Yes, hospice without documented decline may indicate Medicare fraud."),
95
+ (r"(?i).*?\b(different|contradicts|conflicts|inconsistent)\b.*?\b(records|documentation|testimony|statements)\b.*?",
96
+ "Records show {} {} about condition. Is this fraudulent?",
97
+ "Yes, contradictory records suggest fraudulent misrepresentation.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  ]
99
 
100
  for pattern, input_template, output_text in patterns:
101
+ for match in re.finditer(pattern, text):
 
102
  groups = match.groups()
103
  if len(groups) >= 2:
104
+ pairs.append({"input": input_template.format(*groups), "output": output_text})
 
 
 
 
105
 
106
  if not pairs:
107
  if any(x in text.lower() for x in ["medication", "prescribed", "administered"]):
108
  pairs.append({
109
+ "input": "Medication records show inconsistent times. Is this concerning?",
110
+ "output": "Yes, inconsistent timing may indicate fraud or mismanagement."
111
  })
112
  if any(x in text.lower() for x in ["visit", "family", "spouse"]):
113
  pairs.append({
114
+ "input": "Staff documents visits inconsistently. Is this suspicious?",
115
+ "output": "Yes, selective documentation suggests fraudulent record-keeping."
116
  })
117
  if any(x in text.lower() for x in ["hospice", "terminal", "prognosis"]):
118
  pairs.append({
119
+ "input": "Patient on hospice without decline. Is this fraud?",
120
+ "output": "Yes, lack of decline suggests fraudulent certification."
121
  })
122
 
123
  return pairs
124
 
125
+ # Function to process files and train
126
  def train_ui(files):
127
  try:
128
  raw_text = ""
 
197
  except Exception as e:
198
  return f"Error: {str(e)}. Please check file format, dependencies, or the LLama token."
199
 
200
+ # Function to analyze documents
201
  def analyze_document_ui(files):
202
  try:
203
  if not files:
204
+ return "Error: No file uploaded. Please upload a PDF."
205
 
206
  file = files[0]
207
  if not file.name.endswith(".pdf"):
208
+ return "Error: Please upload a PDF file."
209
 
210
  raw_text = ""
211
  with pdfplumber.open(file.name) as pdf:
 
213
  raw_text += page.extract_text() or ""
214
 
215
  if not raw_text:
216
+ return "Error: Could not extract text from PDF."
217
 
218
  analyzer = HealthcareFraudAnalyzer(model, tokenizer)
219
  results = analyzer.analyze_document(raw_text)
220
  return results["summary"]
221
 
222
  except Exception as e:
223
+ return f"Error during analysis: {str(e)}"
224
 
225
+ # Gradio UI
226
  with gr.Blocks(title="Healthcare Fraud Detection Suite") as demo:
227
  gr.Markdown("# Healthcare Fraud Detection Suite")
228
 
229
  with gr.Tabs():
230
  with gr.TabItem("Fine-Tune Model"):
231
+ gr.Markdown("## Train Llama 4 for Fraud Detection")
232
+ gr.Markdown("Upload PDFs or JSON with training pairs.")
233
  train_file_input = gr.File(label="Upload Files (PDF/JSON)", file_count="multiple")
234
  train_button = gr.Button("Start Fine-Tuning")
235
  train_output = gr.Textbox(label="Training Status", lines=5)
236
  train_button.click(fn=train_ui, inputs=train_file_input, outputs=train_output)
237
 
238
  with gr.TabItem("Analyze Document"):
239
+ gr.Markdown("## Analyze for Fraud Indicators")
240
+ gr.Markdown("Upload a PDF to scan for fraud, neglect, or abuse.")
241
+ analyze_file_input = gr.File(label="Upload PDF")
242
  analyze_button = gr.Button("Analyze Document")
243
  analyze_output = gr.Markdown(label="Analysis Results")
244
  analyze_button.click(fn=analyze_document_ui, inputs=analyze_file_input, outputs=analyze_output)
245
 
246
  gr.Markdown("""
247
  ### About This Tool
248
+ Uses Llama 4 Maverick to detect fraud in healthcare documents.
249
+ Fine-tune with custom data or analyze PDFs for suspicious patterns.
250
+ **Note:** All analysis is local - no data is shared.
 
251
  """)
252
 
253
+ # Launch the app
254
  demo.launch()