|
|
import gradio as gr |
|
|
import json |
|
|
from clinical_ner import ClinicalNERProcessor |
|
|
|
|
|
|
|
|
ner_processor = ClinicalNERProcessor(use_pos=True, use_anatomy=True) |
|
|
|
|
|
|
|
|
EXAMPLE_TEXT = "Patient presents with pain in the left ventricle and elevated cardiac enzymes. The heart shows signs of inflammation." |
|
|
|
|
|
def format_entities(entities): |
|
|
"""Format entities for display""" |
|
|
if not entities: |
|
|
return "No entities found." |
|
|
|
|
|
result = [] |
|
|
for i, entity in enumerate(entities, 1): |
|
|
result.append(f"{i}. **{entity['word']}** - Type: {entity['entity_group']} (Score: {entity['score']:.4f})") |
|
|
return "\n".join(result) |
|
|
|
|
|
def format_pos_tags(pos_tags): |
|
|
"""Format POS tags for display""" |
|
|
if not pos_tags: |
|
|
return "No POS tags found." |
|
|
|
|
|
result = [] |
|
|
for i, tag in enumerate(pos_tags, 1): |
|
|
result.append(f"{i}. **{tag['token']}** - POS: {tag['pos']}, Tag: {tag['tag']}, Lemma: {tag['lemma']}") |
|
|
return "\n".join(result) |
|
|
|
|
|
def clinical_ner_basic(text): |
|
|
"""Clinical NER only""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
entities = ner_processor.basic_ner(text) |
|
|
return format_entities(entities) |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
def clinical_ner_prolog(text): |
|
|
"""Clinical NER as Prolog facts""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
prolog_facts = ner_processor.prolog_ner(text) |
|
|
return prolog_facts if prolog_facts else "No entities found." |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
def anatomy_ner_basic(text): |
|
|
"""Anatomy NER only""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
entities = ner_processor.anatomy_ner(text) |
|
|
return format_entities(entities) |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
def anatomy_ner_prolog(text): |
|
|
"""Anatomy NER as Prolog facts""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
prolog_facts = ner_processor.prolog_anatomy(text) |
|
|
return prolog_facts if prolog_facts else "No entities found." |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
def pos_tagging_basic(text): |
|
|
"""POS tagging only""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
pos_tags = ner_processor.pos_tagging(text) |
|
|
return format_pos_tags(pos_tags) |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
def pos_tagging_prolog(text): |
|
|
"""POS tagging as Prolog facts""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
prolog_facts = ner_processor.prolog_pos(text) |
|
|
return prolog_facts if prolog_facts else "No POS tags found." |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
def combined_analysis(text): |
|
|
"""Combined analysis""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text.", "Please enter some text.", "Please enter some text." |
|
|
try: |
|
|
result = ner_processor.combined_analysis(text) |
|
|
clinical = format_entities(result['clinical_entities']) |
|
|
anatomy = format_entities(result['anatomy_entities']) |
|
|
pos = format_pos_tags(result['pos_tags']) |
|
|
return clinical, anatomy, pos |
|
|
except Exception as e: |
|
|
error_msg = f"Error: {str(e)}" |
|
|
return error_msg, error_msg, error_msg |
|
|
|
|
|
def combined_prolog(text): |
|
|
"""Combined analysis as Prolog facts""" |
|
|
if not text.strip(): |
|
|
return "Please enter some text." |
|
|
try: |
|
|
prolog_facts = ner_processor.prolog_combined(text) |
|
|
return prolog_facts if prolog_facts else "No results found." |
|
|
except Exception as e: |
|
|
return f"Error: {str(e)}" |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Clinical NER & Anatomy Detection", theme=gr.themes.Soft()) as demo: |
|
|
gr.Markdown( |
|
|
""" |
|
|
# Clinical NER, Anatomy Detection, and POS Tagging |
|
|
|
|
|
This application provides Named Entity Recognition (NER) for clinical text, |
|
|
anatomy detection, and Part-of-Speech (POS) tagging using state-of-the-art models: |
|
|
- **Clinical NER**: Bio_ClinicalBERT |
|
|
- **Anatomy NER**: OpenMed AnatomyDetect |
|
|
- **POS Tagging**: spaCy en_core_web_sm |
|
|
""" |
|
|
) |
|
|
|
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.Tab("Clinical NER"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
clinical_input = gr.Textbox( |
|
|
label="Enter Clinical Text", |
|
|
placeholder="Enter medical text here...", |
|
|
lines=5, |
|
|
value=EXAMPLE_TEXT |
|
|
) |
|
|
clinical_format = gr.Radio( |
|
|
choices=["Basic", "Prolog"], |
|
|
value="Basic", |
|
|
label="Output Format" |
|
|
) |
|
|
clinical_btn = gr.Button("Extract Clinical Entities", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
clinical_output = gr.Textbox( |
|
|
label="Clinical Entities", |
|
|
lines=15, |
|
|
show_copy_button=True |
|
|
) |
|
|
|
|
|
def clinical_ner_process(text, format_type): |
|
|
if format_type == "Basic": |
|
|
return clinical_ner_basic(text) |
|
|
else: |
|
|
return clinical_ner_prolog(text) |
|
|
|
|
|
clinical_btn.click( |
|
|
fn=clinical_ner_process, |
|
|
inputs=[clinical_input, clinical_format], |
|
|
outputs=clinical_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Anatomy Detection"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
anatomy_input = gr.Textbox( |
|
|
label="Enter Clinical Text", |
|
|
placeholder="Enter medical text here...", |
|
|
lines=5, |
|
|
value=EXAMPLE_TEXT |
|
|
) |
|
|
anatomy_format = gr.Radio( |
|
|
choices=["Basic", "Prolog"], |
|
|
value="Basic", |
|
|
label="Output Format" |
|
|
) |
|
|
anatomy_btn = gr.Button("Detect Anatomy", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
anatomy_output = gr.Textbox( |
|
|
label="Anatomy Entities", |
|
|
lines=15, |
|
|
show_copy_button=True |
|
|
) |
|
|
|
|
|
def anatomy_ner_process(text, format_type): |
|
|
if format_type == "Basic": |
|
|
return anatomy_ner_basic(text) |
|
|
else: |
|
|
return anatomy_ner_prolog(text) |
|
|
|
|
|
anatomy_btn.click( |
|
|
fn=anatomy_ner_process, |
|
|
inputs=[anatomy_input, anatomy_format], |
|
|
outputs=anatomy_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("POS Tagging"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
pos_input = gr.Textbox( |
|
|
label="Enter Text", |
|
|
placeholder="Enter text here...", |
|
|
lines=5, |
|
|
value=EXAMPLE_TEXT |
|
|
) |
|
|
pos_format = gr.Radio( |
|
|
choices=["Basic", "Prolog"], |
|
|
value="Basic", |
|
|
label="Output Format" |
|
|
) |
|
|
pos_btn = gr.Button("Tag POS", variant="primary") |
|
|
|
|
|
with gr.Column(): |
|
|
pos_output = gr.Textbox( |
|
|
label="POS Tags", |
|
|
lines=15, |
|
|
show_copy_button=True |
|
|
) |
|
|
|
|
|
def pos_process(text, format_type): |
|
|
if format_type == "Basic": |
|
|
return pos_tagging_basic(text) |
|
|
else: |
|
|
return pos_tagging_prolog(text) |
|
|
|
|
|
pos_btn.click( |
|
|
fn=pos_process, |
|
|
inputs=[pos_input, pos_format], |
|
|
outputs=pos_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("Combined Analysis"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
combined_input = gr.Textbox( |
|
|
label="Enter Clinical Text", |
|
|
placeholder="Enter medical text here...", |
|
|
lines=5, |
|
|
value=EXAMPLE_TEXT |
|
|
) |
|
|
combined_format = gr.Radio( |
|
|
choices=["Basic (Separated)", "Prolog (Combined)"], |
|
|
value="Basic (Separated)", |
|
|
label="Output Format" |
|
|
) |
|
|
combined_btn = gr.Button("Analyze All", variant="primary") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
combined_clinical = gr.Textbox( |
|
|
label="Clinical Entities", |
|
|
lines=10, |
|
|
show_copy_button=True, |
|
|
visible=True |
|
|
) |
|
|
|
|
|
with gr.Column(): |
|
|
combined_anatomy = gr.Textbox( |
|
|
label="Anatomy Entities", |
|
|
lines=10, |
|
|
show_copy_button=True, |
|
|
visible=True |
|
|
) |
|
|
|
|
|
with gr.Column(): |
|
|
combined_pos = gr.Textbox( |
|
|
label="POS Tags", |
|
|
lines=10, |
|
|
show_copy_button=True, |
|
|
visible=True |
|
|
) |
|
|
|
|
|
combined_prolog_output = gr.Textbox( |
|
|
label="Combined Prolog Output", |
|
|
lines=20, |
|
|
show_copy_button=True, |
|
|
visible=False |
|
|
) |
|
|
|
|
|
def combined_process(text, format_type): |
|
|
if format_type == "Basic (Separated)": |
|
|
clinical, anatomy, pos = combined_analysis(text) |
|
|
return { |
|
|
combined_clinical: gr.update(value=clinical, visible=True), |
|
|
combined_anatomy: gr.update(value=anatomy, visible=True), |
|
|
combined_pos: gr.update(value=pos, visible=True), |
|
|
combined_prolog_output: gr.update(visible=False) |
|
|
} |
|
|
else: |
|
|
prolog = combined_prolog(text) |
|
|
return { |
|
|
combined_clinical: gr.update(visible=False), |
|
|
combined_anatomy: gr.update(visible=False), |
|
|
combined_pos: gr.update(visible=False), |
|
|
combined_prolog_output: gr.update(value=prolog, visible=True) |
|
|
} |
|
|
|
|
|
combined_btn.click( |
|
|
fn=combined_process, |
|
|
inputs=[combined_input, combined_format], |
|
|
outputs=[combined_clinical, combined_anatomy, combined_pos, combined_prolog_output] |
|
|
) |
|
|
|
|
|
gr.Markdown( |
|
|
""" |
|
|
--- |
|
|
### Models Used: |
|
|
- Clinical NER: `samrawal/bert-base-uncased_clinical-ner` |
|
|
- Anatomy Detection: `OpenMed/OpenMed-NER-AnatomyDetect-BioPatient-108M` |
|
|
- POS Tagging: spaCy `en_core_web_sm` |
|
|
""" |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |