gorkem371 commited on
Commit
8a7ad0e
·
verified ·
1 Parent(s): e20644c

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +143 -58
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import gradio as gr
2
  import torch
3
  import torch.nn.functional as F
 
4
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
5
 
6
  MODEL_ID = "gorkem371/pii-intent-classifier-xlmr-large"
@@ -11,37 +12,71 @@ model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID)
11
  model.eval()
12
  print("Model loaded!")
13
 
14
- ENTITY_TYPES = [
15
- "PHONE", "EMAIL", "SOCIAL_MEDIA", "IBAN", "CREDIT_CARD",
16
- "ADDRESS", "URL", "CRYPTO_ADDRESS", "OFF_PLATFORM_ATTEMPT"
17
- ]
18
 
19
- EXAMPLES = [
20
- ["my number is 05321234567 call me anytime", "05321234567", "PHONE"],
21
- ["your order number is ORD-784321", "ORD-784321", "PHONE"],
22
- ["numaram 05321234567 bana yazabilirsin", "05321234567", "PHONE"],
23
- ["رقمي 0501234567 تواصل معي", "0501234567", "PHONE"],
24
- ["follow me on instagram @creator_official", "@creator_official", "SOCIAL_MEDIA"],
25
- ["i will send you my whatsapp tomorrow", "NONE", "PHONE"],
26
- ["05321234567 this number is a scammer do not call", "05321234567", "PHONE"],
27
- ["bake at 180 degrees celsius for 45 minutes", "180", "PHONE"],
28
- ["check my bio my number is right there", "NONE", "PHONE"],
29
- ["email me at creator@gmail.com", "creator@gmail.com", "EMAIL"],
30
- ["my IBAN is GB29NWBK60161331926819", "GB29NWBK60161331926819", "IBAN"],
31
- ["siparis numaraniz SPN-78432156", "SPN-78432156", "PHONE"],
32
- ["if we agree on terms i will share my number", "NONE", "PHONE"],
33
- ["05321234567 times 2 equals what", "05321234567", "PHONE"],
34
- ["رقمي 0501234567 هههه كأني بعطيك ياخي بس هو كذا", "0501234567", "PHONE"],
35
  ]
36
 
37
-
38
- def classify(context: str, entity: str, entity_type: str):
39
- if not context.strip():
40
- return {}, "Please enter a message."
41
-
42
- if not entity.strip():
43
- entity = "NONE"
44
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  text = f"{context} </s> {entity} | {entity_type}"
46
  inputs = tokenizer(text, max_length=256, padding="max_length", truncation=True, return_tensors="pt")
47
 
@@ -51,20 +86,79 @@ def classify(context: str, entity: str, entity_type: str):
51
 
52
  pii_prob = probs[0][1].item()
53
  not_pii_prob = probs[0][0].item()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  label_scores = {
56
- "🚨 PII DETECTED": pii_prob,
57
- "✅ NOT PII": not_pii_prob,
58
  }
59
 
60
- if pii_prob > not_pii_prob:
61
- verdict = f"🚨 **PII DETECTED** — This message contains PII sharing intent.\n\nConfidence: **{pii_prob:.1%}**"
 
 
 
 
 
 
 
 
 
62
  else:
63
- verdict = f"✅ **NOT PII** — No PII sharing intent detected.\n\nConfidence: **{not_pii_prob:.1%}**"
 
 
 
64
 
65
  return label_scores, verdict
66
 
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  with gr.Blocks(
69
  title="PII Intent Classifier",
70
  theme=gr.themes.Soft(),
@@ -74,64 +168,55 @@ with gr.Blocks(
74
  # 🔍 PII Intent Classifier
75
  ### Multilingual context-aware PII detection (Turkish, Arabic, English)
76
 
77
- This model detects whether a message contains **intent to share personal contact information**.
78
- Unlike regex-based detection, it understands context: sarcasm, negation, scam warnings, and evasion techniques.
79
 
80
- **Model:** [gorkem371/pii-intent-classifier-xlmr-large](https://huggingface.co/gorkem371/pii-intent-classifier-xlmr-large) (XLM-RoBERTa Large, 550M params)
81
  """
82
  )
83
 
84
  with gr.Row():
85
  with gr.Column(scale=2):
86
- context_input = gr.Textbox(
87
  label="Message",
88
- placeholder="Enter a message to analyze...",
89
  lines=3,
90
  )
91
- with gr.Row():
92
- entity_input = gr.Textbox(
93
- label="Entity",
94
- placeholder="e.g. 05321234567 or NONE",
95
- value="NONE",
96
- )
97
- entity_type_input = gr.Dropdown(
98
- choices=ENTITY_TYPES,
99
- label="Entity Type",
100
- value="PHONE",
101
- )
102
- submit_btn = gr.Button("Analyze", variant="primary", size="lg")
103
 
104
  with gr.Column(scale=1):
105
  label_output = gr.Label(label="Classification", num_top_classes=2)
106
- verdict_output = gr.Markdown(label="Verdict")
107
 
108
  submit_btn.click(
109
  fn=classify,
110
- inputs=[context_input, entity_input, entity_type_input],
111
  outputs=[label_output, verdict_output],
112
  )
113
 
114
- context_input.submit(
115
  fn=classify,
116
- inputs=[context_input, entity_input, entity_type_input],
117
  outputs=[label_output, verdict_output],
118
  )
119
 
120
  gr.Examples(
121
  examples=EXAMPLES,
122
- inputs=[context_input, entity_input, entity_type_input],
123
  outputs=[label_output, verdict_output],
124
  fn=classify,
125
  cache_examples=False,
126
- label="Try these examples",
127
  )
128
 
129
  gr.Markdown(
130
  """
131
  ---
132
- **How it works:** Input format is `{message} </s> {entity} | {entity_type}`. Use `NONE` as entity for implicit PII intent (e.g., "I'll send my number later").
 
 
133
 
134
- **Author:** [Gorkem Yildiz](https://gorkemyildiz.com) | **Accuracy:** 97.7% on 177 multilingual stress tests
135
  """
136
  )
137
 
 
1
  import gradio as gr
2
  import torch
3
  import torch.nn.functional as F
4
+ import re
5
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
6
 
7
  MODEL_ID = "gorkem371/pii-intent-classifier-xlmr-large"
 
12
  model.eval()
13
  print("Model loaded!")
14
 
15
+ # ── Auto-detect entities from text ──────────────────────────────
 
 
 
16
 
17
+ ENTITY_PATTERNS = [
18
+ ("EMAIL", r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'),
19
+ ("IBAN", r'\b[A-Z]{2}\d{2}[A-Z0-9]{10,30}\b'),
20
+ ("PHONE", r'(?:\+?\d{1,3}[\s-]?)?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{3,4}\b'),
21
+ ("SOCIAL_MEDIA", r'@[a-zA-Z0-9_.]{2,30}'),
22
+ ("URL", r'(?:https?://)?(?:www\.)?[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(?:/\S*)?'),
23
+ ("CRYPTO_ADDRESS", r'\b(?:0x[a-fA-F0-9]{40}|[13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-zA-HJ-NP-Z0-9]{39,59})\b'),
 
 
 
 
 
 
 
 
 
24
  ]
25
 
26
+ INTENT_KEYWORDS = {
27
+ "PHONE": [
28
+ "numaram", "numara", "whatsapp", "telefon", "ara beni", "bana ulaş",
29
+ "رقمي", "واتساب", "اتصل", "كلمني",
30
+ "my number", "call me", "whatsapp me", "reach me", "contact me",
31
+ "send you my number", "numara atarim", "numara veririm",
32
+ "بارسل لك رقمي", "بعطيك رقمي",
33
+ ],
34
+ "SOCIAL_MEDIA": [
35
+ "instagram", "insta", "tiktok", "telegram", "snapchat", "snap",
36
+ "twitter", "takip et", "follow me", "تابعني", "انستقرام", "سناب",
37
+ "beni bul", "find me", "تلقاني", "add me",
38
+ ],
39
+ "EMAIL": ["mail", "email", "ايميل", "راسلني", "e-posta"],
40
+ "OFF_PLATFORM_ATTEMPT": [
41
+ "başka yerde", "platform dışı", "dışarıda", "oradan konuşalım",
42
+ "let's talk on", "move to", "نروح", "بره", "خارج",
43
+ "bio", "story", "profil", "بايو", "ستوري",
44
+ ],
45
+ }
46
+
47
+
48
+ def detect_entities(text):
49
+ """Auto-detect entities in text."""
50
+ found = []
51
+
52
+ for etype, pattern in ENTITY_PATTERNS:
53
+ for match in re.finditer(pattern, text):
54
+ found.append((match.group(), etype, match.start()))
55
+
56
+ # Remove overlapping matches (keep longest)
57
+ found.sort(key=lambda x: (-len(x[0]), x[2]))
58
+ used_ranges = []
59
+ filtered = []
60
+ for entity, etype, start in found:
61
+ end = start + len(entity)
62
+ if not any(s <= start < e or s < end <= e for s, e in used_ranges):
63
+ filtered.append((entity, etype))
64
+ used_ranges.append((start, end))
65
+
66
+ return filtered
67
+
68
+
69
+ def detect_intent_type(text):
70
+ """If no entity found, detect intent type from keywords."""
71
+ text_lower = text.lower()
72
+ for etype, keywords in INTENT_KEYWORDS.items():
73
+ if any(kw in text_lower for kw in keywords):
74
+ return etype
75
+ return "PHONE"
76
+
77
+
78
+ def predict_single(context, entity, entity_type):
79
+ """Run model on a single entity."""
80
  text = f"{context} </s> {entity} | {entity_type}"
81
  inputs = tokenizer(text, max_length=256, padding="max_length", truncation=True, return_tensors="pt")
82
 
 
86
 
87
  pii_prob = probs[0][1].item()
88
  not_pii_prob = probs[0][0].item()
89
+ return pii_prob, not_pii_prob
90
+
91
+
92
+ def classify(message: str):
93
+ if not message.strip():
94
+ return {}, "Please enter a message."
95
+
96
+ entities = detect_entities(message)
97
+
98
+ if not entities:
99
+ # No explicit entity — check for intent
100
+ intent_type = detect_intent_type(message)
101
+ entities = [("NONE", intent_type)]
102
+
103
+ # Run model on all detected entities, take the highest PII score
104
+ best_pii = 0
105
+ best_not_pii = 1
106
+ best_entity = ""
107
+ best_type = ""
108
+ all_results = []
109
+
110
+ for entity, etype in entities:
111
+ pii_prob, not_pii_prob = predict_single(message, entity, etype)
112
+ all_results.append((entity, etype, pii_prob))
113
+ if pii_prob > best_pii:
114
+ best_pii = pii_prob
115
+ best_not_pii = not_pii_prob
116
+ best_entity = entity
117
+ best_type = etype
118
 
119
  label_scores = {
120
+ "🚨 PII DETECTED": best_pii,
121
+ "✅ NOT PII": best_not_pii,
122
  }
123
 
124
+ # Build verdict
125
+ if best_pii > best_not_pii:
126
+ verdict = f"🚨 **PII DETECTED** — This message contains PII sharing intent.\n\n"
127
+ else:
128
+ verdict = f"✅ **NOT PII** — No PII sharing intent detected.\n\n"
129
+
130
+ verdict += f"**Confidence:** {max(best_pii, best_not_pii):.1%}\n\n"
131
+
132
+ # Show what was detected
133
+ if len(all_results) == 1 and all_results[0][0] == "NONE":
134
+ verdict += f"📋 No explicit entity found — analyzed message intent as `{best_type}`"
135
  else:
136
+ verdict += "📋 **Detected entities:**\n"
137
+ for ent, etype, prob in all_results:
138
+ icon = "🚨" if prob > 0.5 else "✅"
139
+ verdict += f"- {icon} `{ent}` ({etype}) → {'PII' if prob > 0.5 else 'NOT PII'} ({prob:.0%})\n"
140
 
141
  return label_scores, verdict
142
 
143
 
144
+ EXAMPLES = [
145
+ ["my number is 05321234567 call me anytime"],
146
+ ["numaram 05321234567 bana yazabilirsin"],
147
+ ["رقمي 0501234567 تواصل معي"],
148
+ ["follow me on instagram @creator_official"],
149
+ ["email me at creator@gmail.com"],
150
+ ["i will send you my whatsapp tomorrow"],
151
+ ["check my bio my number is right there"],
152
+ ["your order number is ORD-784321"],
153
+ ["05321234567 this number is a scammer do not call"],
154
+ ["bake at 180 degrees celsius for 45 minutes"],
155
+ ["siparis numaraniz SPN-78432156"],
156
+ ["my number is 00000000000 lol just kidding"],
157
+ ["رقم الطلب ORD-784321"],
158
+ ["if we agree on terms i will share my number"],
159
+ ["my old number was 05321234567 it might still work"],
160
+ ]
161
+
162
  with gr.Blocks(
163
  title="PII Intent Classifier",
164
  theme=gr.themes.Soft(),
 
168
  # 🔍 PII Intent Classifier
169
  ### Multilingual context-aware PII detection (Turkish, Arabic, English)
170
 
171
+ Type any message below. The model will automatically detect entities (phone numbers, emails, handles, etc.)
172
+ and determine if the message contains **intent to share personal contact information**.
173
 
174
+ **Model:** [gorkem371/pii-intent-classifier-xlmr-large](https://huggingface.co/gorkem371/pii-intent-classifier-xlmr-large) — XLM-RoBERTa Large, 550M params, 97.7% accuracy
175
  """
176
  )
177
 
178
  with gr.Row():
179
  with gr.Column(scale=2):
180
+ message_input = gr.Textbox(
181
  label="Message",
182
+ placeholder="Type a message to analyze... (Turkish, Arabic, or English)",
183
  lines=3,
184
  )
185
+ submit_btn = gr.Button("🔍 Analyze", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
186
 
187
  with gr.Column(scale=1):
188
  label_output = gr.Label(label="Classification", num_top_classes=2)
189
+ verdict_output = gr.Markdown(label="Details")
190
 
191
  submit_btn.click(
192
  fn=classify,
193
+ inputs=[message_input],
194
  outputs=[label_output, verdict_output],
195
  )
196
 
197
+ message_input.submit(
198
  fn=classify,
199
+ inputs=[message_input],
200
  outputs=[label_output, verdict_output],
201
  )
202
 
203
  gr.Examples(
204
  examples=EXAMPLES,
205
+ inputs=[message_input],
206
  outputs=[label_output, verdict_output],
207
  fn=classify,
208
  cache_examples=False,
209
+ label="💡 Try these examples",
210
  )
211
 
212
  gr.Markdown(
213
  """
214
  ---
215
+ **How it works:** The model automatically detects entities (phone numbers, emails, @handles, URLs, IBANs) in your message
216
+ using regex, then classifies each one for PII sharing intent using a fine-tuned XLM-RoBERTa Large model.
217
+ If no entity is found, it analyzes the message for implicit intent (e.g., "I'll send my number later").
218
 
219
+ **Author:** [Gorkem Yildiz](https://gorkemyildiz.com) · **Dataset:** [pii-intent-detection-multilingual](https://huggingface.co/datasets/gorkem371/pii-intent-detection-multilingual)
220
  """
221
  )
222