VynoDePal commited on
Commit
286e411
·
verified ·
1 Parent(s): e78a0c9

Upload SENTRA SMS Fraud Detector (DistilBERT + RF)

Browse files
README.md ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ - pt
5
+ - fr
6
+ - multilingual
7
+ license: mit
8
+ tags:
9
+ - text-classification
10
+ - sms
11
+ - fraud-detection
12
+ - smishing
13
+ - distilbert
14
+ - multilingual
15
+ - security
16
+ - africa
17
+ - benin
18
+ datasets:
19
+ - ucirvine/sms_spam
20
+ - ealvaradob/phishing-dataset
21
+ metrics:
22
+ - accuracy
23
+ - f1
24
+ - precision
25
+ - recall
26
+ pipeline_tag: text-classification
27
+ model-index:
28
+ - name: sentra-sms-fraud-detector
29
+ results:
30
+ - task:
31
+ type: text-classification
32
+ name: SMS Fraud Detection
33
+ metrics:
34
+ - name: Accuracy
35
+ type: accuracy
36
+ value: 0.991
37
+ - name: F1
38
+ type: f1
39
+ value: 0.970
40
+ - name: Precision
41
+ type: precision
42
+ value: 0.958
43
+ - name: Recall
44
+ type: recall
45
+ value: 0.983
46
+ ---
47
+
48
+ # SENTRA — SMS Fraud Detector (DistilBERT Multilingual)
49
+
50
+ **SENTRA** is a fine-tuned [DistilBERT multilingual](https://huggingface.co/distilbert-base-multilingual-cased) model for **SMS fraud detection (smishing)**. It classifies SMS messages as either **LEGITIMATE** or **FRAUD** with high accuracy.
51
+
52
+ ## Model Description
53
+
54
+ | Property | Value |
55
+ |---|---|
56
+ | **Base model** | `distilbert-base-multilingual-cased` |
57
+ | **Task** | Binary text classification (fraud vs. legitimate) |
58
+ | **Languages** | English, Portuguese, French (+ 101 others via multilingual base) |
59
+ | **Parameters** | 66M |
60
+ | **Format** | SafeTensors |
61
+ | **License** | MIT |
62
+
63
+ This model was developed as part of the **SENTRA ML** project to combat SMS fraud (smishing) in **Benin and West Africa**, where mobile money services are the primary digital payment method.
64
+
65
+ ## Training
66
+
67
+ ### Dataset
68
+
69
+ | Source | Language | Size |
70
+ |---|---|---|
71
+ | [UCI SMS Spam Collection](https://archive.ics.uci.edu/dataset/228/sms+spam+collection) | English | ~5,600 SMS |
72
+ | MOZ-Smishing | Portuguese | ~1,000 SMS |
73
+ | **Total (after dedup)** | **Multilingual** | **7,663 SMS** |
74
+
75
+ ### NLP Preprocessing
76
+
77
+ Before training, SMS messages were cleaned with:
78
+ - **SMS abbreviation expansion** (~55 abbreviations in EN + FR): `ur` → `your`, `slt` → `salut`
79
+ - **Currency normalization**: `$5000`, `50000 FCFA` → `MONEY_AMOUNT`
80
+ - **Repeated character normalization**: `freeee` → `free`
81
+ - URL, email, and phone number removal
82
+
83
+ ### Training Details
84
+
85
+ | Hyperparameter | Value |
86
+ |---|---|
87
+ | Learning rate | 2e-5 |
88
+ | Epochs | 3 |
89
+ | Batch size | 16 |
90
+ | Max sequence length | 128 tokens |
91
+ | Loss function | **Weighted cross-entropy** (class weights: legit=0.589, fraud=3.299) |
92
+ | Early stopping patience | 2 epochs |
93
+ | Optimizer | AdamW |
94
+
95
+ The **weighted loss** addresses class imbalance (~85% legitimate, ~15% fraud), penalizing missed frauds 5.6× more than false positives.
96
+
97
+ ## Results
98
+
99
+ ### DistilBERT (this model)
100
+
101
+ | Metric | Score |
102
+ |---|---|
103
+ | **Accuracy** | 99.1% |
104
+ | **Precision** | 95.8% |
105
+ | **Recall** | **98.3%** |
106
+ | **F1-Score** | 97.0% |
107
+
108
+ ### Improvement over baseline (no NLP preprocessing)
109
+
110
+ | Metric | Before | After | Gain |
111
+ |---|---|---|---|
112
+ | Recall | 95.7% | **98.3%** | +2.6 |
113
+ | F1-Score | 96.9% | 97.0% | +0.1 |
114
+
115
+ > **Recall is the priority metric**: missing a fraud (false negative) is far more dangerous than a false positive. Our 98.3% recall means only 1.7% of frauds slip through.
116
+
117
+ ## Usage
118
+
119
+ ### Quick Start (Transformers)
120
+
121
+ ```python
122
+ from transformers import pipeline
123
+
124
+ classifier = pipeline(
125
+ "text-classification",
126
+ model="VynoDePal/sentra-sms-fraud-detector",
127
+ top_k=None,
128
+ )
129
+
130
+ # Fraudulent SMS
131
+ result = classifier("URGENT: Your account has been suspended. Call +229-12345678 NOW")
132
+ print(result)
133
+ # [[{'label': 'FRAUD', 'score': 0.92}, {'label': 'LEGITIMATE', 'score': 0.08}]]
134
+
135
+ # Legitimate SMS
136
+ result = classifier("Hey! Are we still on for lunch tomorrow at 2pm?")
137
+ print(result)
138
+ # [[{'label': 'LEGITIMATE', 'score': 0.97}, {'label': 'FRAUD', 'score': 0.03}]]
139
+ ```
140
+
141
+ ### Manual Usage
142
+
143
+ ```python
144
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
145
+ import torch
146
+
147
+ model_name = "VynoDePal/sentra-sms-fraud-detector"
148
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
149
+ model = AutoModelForSequenceClassification.from_pretrained(model_name)
150
+
151
+ text = "Congratulations! You won 1,000,000 FCFA. Send your PIN to claim."
152
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128)
153
+
154
+ with torch.no_grad():
155
+ outputs = model(**inputs)
156
+ probabilities = torch.softmax(outputs.logits, dim=-1)
157
+ fraud_prob = probabilities[0][1].item()
158
+
159
+ print(f"Fraud probability: {fraud_prob:.1%}")
160
+ # Fraud probability: 87.3%
161
+ ```
162
+
163
+ ### With Preprocessing (recommended for best results)
164
+
165
+ For optimal performance, apply the same preprocessing used during training:
166
+
167
+ ```python
168
+ import re
169
+
170
+ SMS_ABBREVIATIONS = {
171
+ "u": "you", "ur": "your", "pls": "please", "plz": "please",
172
+ "acc": "account", "acct": "account", "asap": "as soon as possible",
173
+ "msg": "message", "txt": "text", "amt": "amount",
174
+ "slt": "salut", "bjr": "bonjour", "stp": "s il te plait",
175
+ "svp": "s il vous plait", "mrc": "merci", "cpte": "compte",
176
+ }
177
+
178
+ CURRENCY_PATTERN = re.compile(
179
+ r'[£$€]\s?\d+[,.]?\d*|\d+[,.]?\d*\s?(?:usd|eur|gbp|fcfa|cfa|xof)',
180
+ re.IGNORECASE,
181
+ )
182
+ REPEATED_CHARS = re.compile(r'(.)\1{2,}')
183
+
184
+ def preprocess_sms(text: str) -> str:
185
+ text = text.lower()
186
+ text = CURRENCY_PATTERN.sub("money amount", text)
187
+ text = re.sub(r'http\S+|www\.\S+', '', text)
188
+ words = text.split()
189
+ words = [SMS_ABBREVIATIONS.get(w, w) for w in words]
190
+ text = ' '.join(words)
191
+ text = REPEATED_CHARS.sub(r'\1\1', text)
192
+ return text.strip()
193
+
194
+ # Usage
195
+ raw_sms = "URGENT!!! Ur acc has been SUSPENDED. Call NOW to claim $5000"
196
+ clean_sms = preprocess_sms(raw_sms)
197
+ result = classifier(clean_sms)
198
+ ```
199
+
200
+ ## Ensemble Model (SENTRA Production)
201
+
202
+ In the full SENTRA system, this DistilBERT model is combined with a **Random Forest** classifier using **weighted voting** for even more robust predictions:
203
+
204
+ ```
205
+ Ensemble = 0.65 × DistilBERT + 0.35 × Random Forest
206
+ ```
207
+
208
+ The Random Forest model and the full API are available in the [SENTRA ML repository](https://github.com/VynoDePal/sentra_ml).
209
+
210
+ ## Labels
211
+
212
+ | Label | ID | Description |
213
+ |---|---|---|
214
+ | `LEGITIMATE` | 0 | Normal, safe SMS |
215
+ | `FRAUD` | 1 | Fraudulent / smishing SMS |
216
+
217
+ ## Limitations
218
+
219
+ - **Training data**: Primarily English and Portuguese SMS. French is supported via multilingual base model but with fewer training examples.
220
+ - **Regional focus**: Optimized for West African smishing patterns (mobile money, FCFA, MTN/Moov).
221
+ - **SMS length**: Best for messages ≤128 tokens (~200 characters).
222
+ - **No local languages**: Does not cover Fon, Yoruba, or other Beninese languages.
223
+
224
+ ## Citation
225
+
226
+ ```bibtex
227
+ @misc{sentra2026,
228
+ title={SENTRA: SMS Fraud Detection with Ensemble DistilBERT and Random Forest},
229
+ author={SENTRA Team},
230
+ year={2026},
231
+ url={https://github.com/VynoDePal/sentra_ml}
232
+ }
233
+ ```
config.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "activation": "gelu",
3
+ "architectures": [
4
+ "DistilBertForSequenceClassification"
5
+ ],
6
+ "attention_dropout": 0.1,
7
+ "bos_token_id": null,
8
+ "dim": 768,
9
+ "dropout": 0.1,
10
+ "dtype": "float32",
11
+ "eos_token_id": null,
12
+ "hidden_dim": 3072,
13
+ "id2label": {
14
+ "0": "LEGITIMATE",
15
+ "1": "FRAUD"
16
+ },
17
+ "initializer_range": 0.02,
18
+ "label2id": {
19
+ "FRAUD": 1,
20
+ "LEGITIMATE": 0
21
+ },
22
+ "max_position_embeddings": 512,
23
+ "model_type": "distilbert",
24
+ "n_heads": 12,
25
+ "n_layers": 6,
26
+ "output_past": true,
27
+ "pad_token_id": 0,
28
+ "qa_dropout": 0.1,
29
+ "seq_classif_dropout": 0.2,
30
+ "sinusoidal_pos_embds": false,
31
+ "tie_weights_": true,
32
+ "tie_word_embeddings": true,
33
+ "transformers_version": "5.3.0",
34
+ "use_cache": false,
35
+ "vocab_size": 119547
36
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0f1edd44195611fb1cb1cbd485da85f68239ae5271e259f08c7fcf6c47daaa4b
3
+ size 541317368
sentra_random_forest_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b46cfde17ae0a6e5d64e618255878eb63db6b9b2dfdd82ea70021f191d9a0050
3
+ size 2991252
sentra_tfidf_vectorizer.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2d1db8580e768e02348fc6c6b6112559b05221b34a2a9fa980c700c0911bfedf
3
+ size 184843
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "cls_token": "[CLS]",
4
+ "do_lower_case": false,
5
+ "is_local": false,
6
+ "mask_token": "[MASK]",
7
+ "model_max_length": 512,
8
+ "pad_token": "[PAD]",
9
+ "sep_token": "[SEP]",
10
+ "strip_accents": null,
11
+ "tokenize_chinese_chars": true,
12
+ "tokenizer_class": "BertTokenizer",
13
+ "unk_token": "[UNK]"
14
+ }