gamaly commited on
Commit
5bcf32b
·
verified ·
1 Parent(s): 0a0c412

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +172 -0
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gradio app for Maritime Intelligence Classifier."""
2
+ import gradio as gr
3
+ from setfit import SetFitModel
4
+ from pathlib import Path
5
+ import os
6
+
7
+ # Try to load model from Hugging Face Hub first, then fall back to local
8
+ # Set MODEL_PATH environment variable or update this line with your Hugging Face repo ID
9
+ MODEL_PATH = os.getenv("MODEL_PATH", "gamaly/maritime-intelligence-classifier")
10
+ LOCAL_MODEL_PATH = "./maritime_classifier"
11
+
12
+ # Load model
13
+ print("Loading model...")
14
+ try:
15
+ # Try Hugging Face Hub first
16
+ if "/" in MODEL_PATH and not Path(MODEL_PATH).exists():
17
+ model = SetFitModel.from_pretrained(MODEL_PATH)
18
+ print(f"✓ Loaded model from Hugging Face: {MODEL_PATH}")
19
+ else:
20
+ # Try local path
21
+ if Path(LOCAL_MODEL_PATH).exists():
22
+ model = SetFitModel.from_pretrained(LOCAL_MODEL_PATH)
23
+ print(f"✓ Loaded model from local path: {LOCAL_MODEL_PATH}")
24
+ else:
25
+ raise FileNotFoundError(f"Model not found at {MODEL_PATH} or {LOCAL_MODEL_PATH}")
26
+ except Exception as e:
27
+ print(f"⚠️ Error loading model: {e}")
28
+ print("Make sure the model is trained or uploaded to Hugging Face")
29
+ model = None
30
+
31
+ def predict_text(text):
32
+ """Predict whether text is actionable (YES) or not (NO)."""
33
+ if model is None:
34
+ return "Error: Model not loaded. Please train the model first.", 0.0, "error"
35
+
36
+ if not text or not text.strip():
37
+ return "Please enter some text to classify.", 0.0, "neutral"
38
+
39
+ try:
40
+ # Make prediction
41
+ prediction = model.predict([text])[0]
42
+ probabilities = model.predict_proba([text])[0]
43
+
44
+ # Get confidence
45
+ confidence = probabilities[prediction] * 100
46
+
47
+ # Convert to labels
48
+ label = "YES (Actionable)" if prediction == 1 else "NO (Not Actionable)"
49
+
50
+ # Determine status for styling
51
+ status = "actionable" if prediction == 1 else "not_actionable"
52
+
53
+ return label, confidence, status
54
+ except Exception as e:
55
+ return f"Error during prediction: {str(e)}", 0.0, "error"
56
+
57
+ def get_explanation(status):
58
+ """Get explanation based on prediction status."""
59
+ explanations = {
60
+ "actionable": "✓ This text contains actionable vessel-specific evidence (e.g., specific vessel names, crimes, incidents).",
61
+ "not_actionable": "✗ This text does not contain actionable vessel-specific evidence (e.g., general maritime news, non-specific information).",
62
+ "error": "⚠️ An error occurred. Please check the model is properly loaded.",
63
+ "neutral": ""
64
+ }
65
+ return explanations.get(status, "")
66
+
67
+ # Create Gradio interface
68
+ with gr.Blocks(title="Maritime Intelligence Classifier", theme=gr.themes.Soft()) as app:
69
+ gr.Markdown(
70
+ """
71
+ # 🚢 Maritime Intelligence Classifier
72
+
73
+ Classify maritime news articles as containing **actionable vessel-specific evidence** (YES) or not (NO).
74
+
75
+ **Actionable articles** typically include:
76
+ - Specific vessel names
77
+ - Specific crimes or incidents
78
+ - Evidence that can be used for investigation
79
+
80
+ **Non-actionable articles** are general maritime news without specific vessel details.
81
+ """
82
+ )
83
+
84
+ with gr.Row():
85
+ with gr.Column(scale=2):
86
+ text_input = gr.Textbox(
87
+ label="Article Text",
88
+ placeholder="Paste or type the maritime news article text here...",
89
+ lines=10,
90
+ max_lines=20
91
+ )
92
+
93
+ submit_btn = gr.Button("Classify", variant="primary", size="lg")
94
+
95
+ with gr.Column(scale=1):
96
+ prediction_output = gr.Label(
97
+ label="Prediction",
98
+ value={"YES (Actionable)": 0.0, "NO (Not Actionable)": 0.0}
99
+ )
100
+
101
+ confidence_output = gr.Number(
102
+ label="Confidence",
103
+ value=0.0,
104
+ precision=1
105
+ )
106
+
107
+ explanation_output = gr.Markdown()
108
+
109
+ # Example texts
110
+ gr.Markdown("### 📝 Example Texts")
111
+ with gr.Row():
112
+ example_yes = gr.Examples(
113
+ examples=[
114
+ ["The fishing vessel Marine 707 was involved in the disappearance of fisheries observer Samuel Abayateye in Ghanaian waters. The observer's decapitated body was found weeks later."],
115
+ ["Authorities detained the Meng Xin 15 after discovering evidence of illegal saiko transshipment and threats against fisheries observers."],
116
+ ],
117
+ inputs=text_input,
118
+ label="YES Examples (Actionable)"
119
+ )
120
+
121
+ example_no = gr.Examples(
122
+ examples=[
123
+ ["A new maritime museum opened in the port city, showcasing historical ships and ocean exploration artifacts."],
124
+ ["Marine scientists are studying the effects of ocean acidification on coral reefs in tropical waters."],
125
+ ],
126
+ inputs=text_input,
127
+ label="NO Examples (Not Actionable)"
128
+ )
129
+
130
+ # Connect the prediction function
131
+ def update_prediction(text):
132
+ label, confidence, status = predict_text(text)
133
+
134
+ # Create label dict for gradio Label component
135
+ if status == "actionable":
136
+ label_dict = {"YES (Actionable)": confidence / 100, "NO (Not Actionable)": (100 - confidence) / 100}
137
+ elif status == "not_actionable":
138
+ label_dict = {"YES (Actionable)": (100 - confidence) / 100, "NO (Not Actionable)": confidence / 100}
139
+ else:
140
+ label_dict = {"YES (Actionable)": 0.0, "NO (Not Actionable)": 0.0}
141
+
142
+ explanation = get_explanation(status)
143
+
144
+ return label_dict, confidence, explanation
145
+
146
+ submit_btn.click(
147
+ fn=update_prediction,
148
+ inputs=text_input,
149
+ outputs=[prediction_output, confidence_output, explanation_output]
150
+ )
151
+
152
+ text_input.submit(
153
+ fn=update_prediction,
154
+ inputs=text_input,
155
+ outputs=[prediction_output, confidence_output, explanation_output]
156
+ )
157
+
158
+ gr.Markdown(
159
+ """
160
+ ---
161
+ ### ℹ️ About
162
+
163
+ This classifier uses SetFit to identify maritime news articles containing actionable vessel-specific evidence.
164
+ Built for The Outlaw Ocean Project.
165
+
166
+ **Model**: SetFit (sentence-transformers/all-MiniLM-L6-v2 base)
167
+ """
168
+ )
169
+
170
+ if __name__ == "__main__":
171
+ app.launch(share=False)
172
+