SMS classification intelligence evaluation and targeted model improvement#202
Conversation
…rovement Evaluate the OTS mBERT classifier against public SMS-classification benchmarks and a new multilingual adversarial suite, then close the identified gaps with a targeted synthetic dataset and an incremental fine-tune. Findings (v2.5 baseline): - 99.3% accuracy on the UCI SMS Spam Collection (classic benchmark) - 99.2% block rate on the Mishra & Soni SMS Phishing dataset, but only 2.7% phishing-label recall (smishing routed to spam) - 20.4% three-class / 69.9% block rate on the modern, multilingual IMC 2025 smishing corpus, with ~25% of phishing silently passed as ham - Non-English block rates falling to ~52-56% Improvement (v2.6 incremental fine-tune): - UCI block rate held at 99.2% (no classic-spam regression) - IMC25 block rate 69.9% -> 78.2%, phishing recall 17% -> 40% - Adversarial suite block rate 73.3% -> 98.0%, phishing recall 11.5% -> 82% - Multilingual block-rate gains concentrated where the model was weak (Italian +19.6pts, Japanese +19.7pts, Spanish +13.8pts) Adds an offline, reproducible evaluation harness, the adversarial test set, the Mishra & Soni benchmark loader, a synthetic data generator with locale-correct brands and hard-negative ham twins, an incremental fine-tune script, and a full written report (evals/REPORT.md).
…ardening Training (evals/finetune_tier1.py): - Apple Silicon MPS / CUDA / CPU device auto-detection - stratified train/validation split with per-epoch validation metrics - best-checkpoint selection by validation macro-F1 (fixes the last-epoch regressions from the proof-of-concept run) - class-weighted or focal loss to counter the phishing->spam collapse - warmup + linear LR schedule, gradient clipping Calibration (evals/calibrate_thresholds.py + run_eval --logit-bias): - training-free per-class logit-bias search to recover phishing recall; on the adversarial set this alone lifts block 73%->83% and phishing recall 12%->35% on the unchanged v2.5 weights Obfuscation hardening (production path): - strip zero-width / invisible formatting characters in the preprocessor - run homoglyph + zero-width normalisation in the dynamic-batching path (previously only the per-request path normalised, so the production default never did) Adds evals/TIER1.md runbook for reproducing on Apple Silicon.
|
Hi @ajamous I Went through the eval harness closely — solid, security-relevant work, and wiring normalisation into the batching path is the right production catch. Building on the automated review rather than repeating it: the three Critical items aren't independent — they all distort the same number REPORT.md leans on hardest (IMC25 block / phishing recall). In that light: 1. Happy to send a small PR with the corrected loader once we confirm whether IMC25 has a legit/ham class (which should map to 2. Circular eval — worth stating outright in REPORT.md: fable5 was authored by the same model that generated the fine-tune data, so the 98% adversarial figure is in-distribution. IMC25's +8.3pts is the honest number to lead with. 3. v2.7 loss mismatch — if v2.7 shipped with plain loss rather than the documented |
- ModelInfo gains an 'architecture' field so 'version' is unambiguously the model semver (e.g. 2.7), not the network name; fix misleading schema examples - /health now reports both api_version (platform, e.g. 2.10.0) and a model block (name/version/architecture); 'version' kept as a backward-compatible alias - add model_manager.get_model_version() for a cheap version lookup
…sation tests - Untrack reproducible prediction dumps; gitignore catch-all (review #3) - Vendored BERT vocab -> Git LFS + Apache-2.0 attribution (review #1) - calibrate_thresholds: importlib load (no sys.path mutation) + with-statement (review #4,#5) - run_eval: ap.error instead of assert; DEFAULT_MODEL baseline note (review #8,#9) - finetune_incremental: --num-workers arg, default 0 (macOS safety) (review #7) - settings: OTS_MBERT_MODEL_PATH override for env-driven model rollback (review #2) - health/response_models: clarify architecture/tokenizer key; mark version deprecated (review #10,#11) - add 9 unit tests for the batching-path normalisation + fallback (review #6)
…rf, tests Bugs: - settings: guard _apply_model_path_override against unknown default_mbert_version (KeyError) (#1) - run_eval: warn when scoring the v2.5 baseline default instead of production weights (#3) Quality: - batching_service: log a warning when EnhancedPreprocessor import fails (no silent disable) (#4) - extract shared dataset loaders to evals/loaders.py; run_eval + calibrate import it normally (drops importlib hack) (#5) - drop unreferenced intermediate summaries summary_v2.5_mishra/v2.6/v2.6_big.json (#6) - health: de-rot the architecture/tokenizer-key comment (no review-number ref) (#9) - enhanced_preprocessing: INVISIBLE_CHARS as \u escapes with codepoint comments (set unchanged) (minor) Performance: - batching_service: ASCII fast-path in _normalize_text skips normalisation for typical traffic (#7) API: - response_models: mark HealthResponse.version Field(deprecated=True) (#8) Tests/CI: - add GitHub Actions unit-test workflow running pytest (#12) - add settings-override tests incl. KeyError guard + per-instance isolation (#13) - add ASCII fast-path test; keep raise-swallow test on non-ASCII input (#7) Docs: - TIER1.md: loosen torch pin to stable; flag in-domain validation caveat (minor) - dataset/README_SYNTHETIC.md: mark synthetic data, warn against pipeline misuse (#11)
Addresses CodeQL actions hardening finding: limit GITHUB_TOKEN scope.
|
Saw the v2.10.0-rc.1 / model 2.7 release — solid. The "gentle recipe beats class-weighted because the heavier config overfit IMC25" result is a clean finding worth keeping in REPORT.md, and the explicit "treat 72.4% as validation, UCI/Mishra/adversarial as independent" framing resolves the circular-eval point well. Correction on my earlier point #1: I pulled the public IMC25 set to check the scam_type values before assuming. It's banking / delivery / government / telecom / others / spam / wrong number / hey-mum-dad — no ham/legit class. So The one methodology gap that does stand: IMC25 is all-attack, so the multilingual eval has no legitimate-message control. Block rate there measures attack recall, but there's no signal on non-English false positives — whether the model over-blocks legit non-English SMS, which is the weakest area in the per-language breakdown. A small multilingual ham control set alongside it would close that. Happy to put one together if useful. |
@Frhnfaya Thanks for pulling the IMC25 set to verify rather than assuming, agreed, no ham/legit class in that corpus, so the non-spam→phishing mapping holds. I’ll have the loader skip-and-count the ~81 empty scam_type rows so it’s explicit rather than silent. Yes to the multilingual ham control set. Even a couple hundred legit messages across the weaker languages would let us track per-language false-positive rate next to block rate. If you put it together I’ll wire it into run_eval as its own benchmark and report FP rates per language alongside the existing metrics. On the loss config: plain / lr 1e-5 / 2 epochs was deliberate — class-weighted at 4 epochs overfit IMC25 and dropped block rate to ~65%. I’ll make REPORT.md and TIER1.md state that explicitly so it doesn’t get “corrected” back to the overfitting recipe. |
- load_imc25: skip and count rows with no scam_type instead of silently labelling them phishing; document IMC25 as an all-attack corpus (no ham class), confirming the spam/phishing mapping is correct for it - use md5(usedforsecurity=False) for the deterministic placeholder seed so the loader works under FIPS-enforced environments - REPORT.md: note IMC25 measures attack recall only (no false-positive signal), flag the missing multilingual legitimate-message control as the top next addition, and mark the batching-path obfuscation normalization as done rather than pending
Hi @ajamous . I Put the multilingual ham control set together and ran it through model 2.7 (47 legit Two findings, and the first is reassuring: Conversational ham is clean in all four languages — personal and personal_money Realistic transactional A2P gets classified as spam, and it's language-independent bank_alert 0/7, government 0/7, appointment 0/4 pass; delivery and For a telecom firewall this is exactly the high-value traffic that must pass, so it |
|
Following up with the data work — focused there since that's where the gap is. 1. Measured the current model (2.7). Built an 88-message held-out benchmark of
2. Root cause is in v2.4. Transactional ham is thin and over-templated: government 3. Built a training set to close it. 486 rows: 282 realistic branded A2P ham + 204 Honest caveat: the benchmark and training set are disjoint but both constructed, so the If useful, you could fine-tune on it (mixed with a rehearsal sample, same recipe as the |
Could you please share the steps to reproduce this issue ? Thanks! |
Hi @Frhnfaya Go ahead and open your PR, including steps to reproduce and test. I will take a look ASAP. Thanks again for your contribution. |
@ajamous Reproduction steps and all files are in PR #209. The build scripts are |
…), correct benchmark-independence claims - Remove 23 eval rows matching synthetic_fable5_v1.csv verbatim or at >=0.8 token-set Jaccard, so no adversarial-suite row appears in model 2.7's training data; re-run v2.5 and v2.7 on the decontaminated suite (block 75.6%->96.9%, phishing recall 11.9%->80.6% — gains unchanged, confirming they were not memorization) - REPORT.md: disclose UCI (~42%) and Mishra & Soni (~36%) verbatim overlap with the v2.4 training corpus; reframe both as internal regression gates rather than independent confirmations; update all fable5 figures - Add CC BY 4.0 attribution for the vendored Mishra & Soni dataset
Summary
Evaluates the OpenTextShield mBERT classifier against public SMS-classification benchmarks plus a new multilingual adversarial suite, identifies where its "intelligence" breaks down, and closes those gaps with a targeted synthetic dataset, an incremental fine-tune (shipped as model 2.7), and a set of Tier-1 training/serving improvements — with no regression on classic spam.
All evaluation is offline and reproducible: weights are loaded directly, the mBERT vocab is vendored, and no API server or external model download is required.
Findings (v2.5 baseline)
evals/REPORT.md§2).ham. Conversational ("hi mum"), brand-impersonation (toll/delivery fee, bank lock), vishing-callback, and obfuscated attacks evade detection; non-English block rate falls to ~52–56%.Improvement (shipped model 2.7)
Continues training from v2.5 on a targeted synthetic dataset (modern smishing archetypes + hard-negative ham twins, 15 languages) mixed with a rehearsal sample of the original corpus to prevent catastrophic forgetting. Numbers below are the shipped 2.7 weights, from
evals/results/summary_v2.5.json/summary_v2.7.json:Block rate improves on every benchmark with zero classic-spam regression. Per-language and per-category breakdowns are in
evals/REPORT.md§5.Evidence integrity (2026-07-01 update)
synthetic_fable5_v1.csvverbatim or at ≥0.8 token overlap were removed, and both models re-scored on the clean 127-row suite. The gains are virtually unchanged (block 96.9% vs 96.7% pre-decontamination), confirming they are not memorization.Tier-1 improvements (this branch)
evals/finetune_tier1.py— Apple Silicon MPS / CUDA / CPU auto-detection, stratified train/validation split with per-epoch metrics, best-checkpoint selection by validation macro-F1, and class-weighted / focal loss options.evals/calibrate_thresholds.py+run_eval.py --logit-bias— training-free per-class logit-bias calibration. On the adversarial set this alone lifts block rate and phishing recall substantially on the unchanged v2.5 weights.evals/TIER1.md— runbook to reproduce all of the above on Apple Silicon in minutes.What's included
evals/run_eval.py— offline evaluation harness (3-class accuracy, block rate, per-class P/R/F1, confusion matrix, per-language/category breakdowns; optional logit-bias)evals/datasets/fable5_adversarial_v1.csv— 127-message multilingual adversarial suite (decontaminated against the fine-tune set)evals/datasets/mishra_soni_5971.csv— Mishra & Soni SMS Phishing benchmark + loader (CC BY 4.0, seeevals/assets/ATTRIBUTION.md)evals/generate_synthetic.py+dataset/synthetic_fable5_v1.csv— targeted synthetic data generator and outputevals/finetune_incremental.py/evals/finetune_tier1.py— fine-tune scriptsevals/calibrate_thresholds.py,evals/compare_runs.py— calibration and comparison utilitiesevals/REPORT.md,evals/TIER1.md— written report and Apple-Silicon runbookevals/results/— baseline and post-fine-tune metric summariesModel weights
The fine-tuned weights
mbert_ots_model_2.7.pth(~680 MB) are committed to this branch via Git LFS and the API default points at them. They are also fully reproducible from the included scripts. The shipped recipe is a gentle, overfitting-aware fine-tune (plain loss, lr 1e-5, 2 epochs) trained on Apple Silicon MPS with best-checkpoint selection — heavier configs won the in-domain validation split but regressed the real-world IMC25 block rate. Followevals/TIER1.mdor:python evals/finetune_tier1.py \ --base src/mBERT/training/model-training/mbert_ots_model_2.5.pth \ --synthetic src/mBERT/training/model-training/dataset/synthetic_fable5_v1.csv \ --original src/mBERT/training/model-training/dataset/sms_spam_phishing_dataset_v2.4_combined.csv \ --out src/mBERT/training/model-training/mbert_ots_model_2.7.pth \ --epochs 2 --batch-size 32 --loss plain --lr 1e-5Notes / next steps