Verite / README.md
Daxlia's picture
Update README.md
9f3c6a7 verified
|
Raw
History Blame Contribute Delete
8.89 kB
---
language:
- en
license: mit
tags:
- text-classification
- deception-detection
- fake-news
- spam-detection
- modernbert
- pytorch
- multi-task-learning
- cross-domain
base_model: answerdotai/ModernBERT-base
datasets:
- difraud/difraud
metrics:
- f1
- roc_auc
pipeline_tag: text-classification
---
# Verite!: Cross-Domain Deception Detection
**Verite!** is a cross-domain deception detection classifier built on [ModernBERT-base](https://huggingface.co/answerdotai/ModernBERT-base). It detects deceptive content (fake news, phishing, spam, job scams, political misinformation, product review fraud, and Twitter rumours) with a single unified model.
> Code and training script: [github.com/Daxlia/Verite](https://github.com/Daxlia/Verite)
Evaluated on the [DIFrauD](https://huggingface.co/datasets/difraud/difraud) benchmark — 7 domains, ~103K samples.
---
## Results
![System comparison](comparison_chart.png)
Evaluated on the DIFrauD test set (macro-F1 and AUC-ROC, higher is better).
| System | Macro-F1 | AUC-ROC |
| ------ | -------- | ------- |
| Majority class | 0.3792 | 0.5000 |
| TF-IDF + LR | 0.8094 | 0.9079 |
| **Verite! (ours)** | **0.8512** | **0.9487** |
| SOTA (DIFrauD leaderboard) | 0.904 | — |
> Single seed (seed=42) on 2×NVIDIA T4. Multi-seed ensemble (3 seeds) expected to improve further.
### Per-Domain Breakdown
![Per-domain performance](per_domain_chart.png)
| Domain | Macro-F1 | AUC-ROC | Accuracy |
| ------ | -------- | ------- | -------- |
| Fake News | 0.9855 | 0.9990 | 0.9858 |
| Job Scams | 0.7584 | 0.9354 | 0.9636 |
| Phishing | 0.9708 | 0.9964 | 0.9719 |
| Political Statements | 0.4562 | 0.6418 | 0.6504 |
| Product Reviews | 0.6424 | 0.7653 | 0.6582 |
| SMS Spam | 0.9834 | 0.9998 | 0.9894 |
| Twitter Rumours | 0.7856 | 0.8800 | 0.7945 |
### Ablation Study
![Ablation study](ablation_chart.png)
| Variant | Macro-F1 | AUC-ROC |
| ------- | -------- | ------- |
| Verite! (full) | 0.8512 | 0.9487 |
| w/o Linguistic Features | 0.8544 | 0.9475 |
| TTA | 0.8515 | 0.9456 |
---
## Model Description
Verite! extends ModernBERT-base with four feature streams fused before classification:
| Component | Output | Role |
| --------- | ------ | ---- |
| **AttentionPooling + semantic_proj** | 512-d | Weighted token aggregation → projected semantic embedding |
| **ling_proj** | 512-d | 8 hand-crafted linguistic features (caps ratio, punctuation, TTR, hedge words, negation…) projected to hidden dim |
| **LocalConsistencyModule** | 256-d | Splits sequence into 4 segments; cross-attention detects internal contradictions |
| **SpectralFeatures** | 10-d | Top-8 FFT magnitudes + spectral centroid + entropy over the token sequence |
All four streams are concatenated (512+512+256+10 = **1290-d**), projected through `LayerNorm → Linear(512) → GELU`, then through 5× multi-sample dropout into a **HypersphericalHead** (prototype classifier on the unit hypersphere, scale=20).
**Architecture diagram:**
```text
Input Text ──────────────────────── Linguistic Features (8-d)
│ │
▼ ▼
ModernBERT-base (149M params, fp32) ling_proj → feat_emb (512-d)
│ │
├── AttentionPooling ──► semantic_proj ──► sem_emb (512-d) ──┐
├── LocalConsistencyModule (4 segs) ──► cons_emb (256-d) ─────┤
└── SpectralFeatures (top-8 FFT + centroid + entropy) ► spec (10-d)
Concatenate [sem_emb | feat_emb | cons_emb | spec] (1290-d)
LayerNorm → Linear(512) → GELU
Multi-sample Dropout (5×) → HypersphericalHead
Logits (2 classes)
```
---
## Training
Trained on 2×NVIDIA T4 (16 GB each) via Kaggle (~13 h, < 14 h total session). The encoder always runs in fp32 to avoid SDPA overflow on sm75 hardware; head layers use fp16 via PyTorch AMP.
**Training objectives:**
- Focal loss (γ=2.0) with class-balanced weights and label smoothing (ε=0.05)
- Supervised Contrastive loss (λ=0.1, τ=0.07) on semantic embeddings
- Domain MTL cross-entropy (λ=0.1) across 7 domain heads
**Regularization stack:**
- AdamW + LLRD (decay=0.9): encoder LR=1e-5, head LR=1e-4
- Cosine schedule with 8% linear warmup
- Gradient accumulation (×4), gradient clipping (0.7)
- EMA (decay=0.995) — used for final checkpoint selection
- Adversarial Weight Perturbation (AWP, ε=0.001) from epoch 4 onward
**Hyperparameters:**
| Parameter | Value |
| --------- | ----- |
| Base encoder | `answerdotai/ModernBERT-base` |
| Max sequence length | 256 |
| Effective batch size | 64 (8 × 2 GPUs × 4 accum steps) |
| Epochs | 5 |
| Encoder LR | 1e-5 |
| Head LR | 1e-4 |
| LLRD decay | 0.9 |
| Warmup | 8% |
| Weight decay | 0.02 |
| Focal γ | 2.0 |
| SupCon λ | 0.1 |
| Domain MTL λ | 0.1 |
| AWP start | Epoch 4 |
| EMA decay | 0.995 |
| Total runtime | ~13 h on 2×T4 (< 14 h) |
---
## Usage
> **Important:** This model uses a custom architecture defined in `VeriteTrainer.py`. You must have that file in your Python path before loading.
### Installation
```bash
pip install torch>=2.1.0 transformers>=4.47.0 safetensors sentencepiece
```
### Inference
```python
import torch
from torch.utils.data import DataLoader
from transformers import AutoTokenizer
from safetensors.torch import load_file
from VeriteTrainer import DeceptionReasoningModel, Config, DeceptionDataset, collate_fn
cfg = Config()
tokenizer = AutoTokenizer.from_pretrained("Daxlia/verite")
model = DeceptionReasoningModel(cfg)
model.load_state_dict(load_file("model.safetensors"))
model.eval()
texts = ["Congratulations! You've been selected to receive a $1,000 gift card. Click here to claim."]
dataset = DeceptionDataset(texts, [0] * len(texts), tokenizer, cfg)
loader = DataLoader(dataset, batch_size=8, shuffle=False, collate_fn=collate_fn)
with torch.no_grad():
for batch in loader:
out = model(input_ids=batch["input_ids"],
attention_mask=batch["attention_mask"],
ling_feats=batch["ling_feats"])
prob = torch.softmax(out["logits"], dim=-1)[:, 1]
for t, p in zip(texts, prob.tolist()):
label = "DECEPTIVE" if p > 0.5 else "GENUINE"
print(f"[{label}] P(deceptive) = {p:.4f} | {t}")
```
### Output format
| Key | Shape | Description |
| --- | ----- | ----------- |
| `logits` | `(B, 2)` | Raw class logits (genuine=0, deceptive=1) |
| `sem_emb` | `(B, 512)` | Semantic embedding (useful for downstream tasks) |
| `domain_logits` | `(B, 7)` | Domain classification logits |
---
## Domains
The model was trained jointly on all 7 DIFrauD domains:
| Domain | Description |
| ------ | ----------- |
| `fake_news` | News articles with fabricated or misleading content |
| `job_scams` | Fraudulent job postings |
| `phishing` | Phishing emails and URLs |
| `political_statements` | Fact-checked political claims |
| `product_reviews` | Fake/incentivised product reviews |
| `sms` | SMS spam messages |
| `twitter_rumours` | Unverified claims spread on Twitter/X |
---
## Limitations
- Max input length is 256 tokens; longer texts are truncated.
- Inference requires `VeriteTrainer.py` — no `AutoModel`-compatible registration yet.
- Performance is weakest on Political Statements (F1=0.456) and Product Reviews (F1=0.642) — domains with subtle, opinion-heavy deception signals.
- Results reported for a single training seed; variance across seeds has not been fully characterized.
---
## Citation
```bibtex
@misc{verite2026,
author = {Daxlia},
title = {Verite!: Cross-Domain Deception Detection with ModernBERT},
year = {2026},
doi = {10.5281/zenodo.20256648},
url = {https://doi.org/10.5281/zenodo.20256648}
}
```
---
## AI Disclosure
This project was developed with assistance from AI tools:
- **ChatGPT (OpenAI)** — Bug identification, algorithmic suggestions, and research ideation
- **Claude (Anthropic)** — Code implementation, debugging, and writing of documentation files
All AI-generated content was reviewed, validated, and adapted by the author.
---
## License
This model is released under the [MIT License](https://opensource.org/licenses/MIT).
The base encoder [ModernBERT-base](https://huggingface.co/answerdotai/ModernBERT-base) is licensed under Apache 2.0.
The [DIFrauD dataset](https://huggingface.co/datasets/difraud/difraud) is subject to its own license; refer to the dataset repository.