Zero-Shot Classification
Transformers
ONNX
Safetensors
English
roberta
text-classification
nli
compliance
popia
south-africa
privacy
legal
quantized
cpu
Instructions to use labrat-aiko/nli-popia-v1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use labrat-aiko/nli-popia-v1 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-classification", model="labrat-aiko/nli-popia-v1")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("labrat-aiko/nli-popia-v1") model = AutoModelForSequenceClassification.from_pretrained("labrat-aiko/nli-popia-v1", device_map="auto") - Notebooks
- Google Colab
- Kaggle
docs: polish model card — link dataset, add limitations, raw ONNX snippet, label-index callout
Browse files
README.md
CHANGED
|
@@ -3,35 +3,43 @@ language: en
|
|
| 3 |
license: apache-2.0
|
| 4 |
library_name: transformers
|
| 5 |
base_model: cross-encoder/nli-MiniLM2-L6-H768
|
|
|
|
|
|
|
| 6 |
tags:
|
| 7 |
- nli
|
| 8 |
- zero-shot-classification
|
| 9 |
- compliance
|
| 10 |
- popia
|
| 11 |
- south-africa
|
|
|
|
|
|
|
| 12 |
- onnx
|
| 13 |
- quantized
|
|
|
|
| 14 |
pipeline_tag: zero-shot-classification
|
| 15 |
---
|
| 16 |
|
| 17 |
# nli-popia-v1
|
| 18 |
|
| 19 |
-
Cross-encoder NLI model fine-tuned for
|
| 20 |
-
**POPIA** (South Africa's Protection of
|
| 21 |
-
|
|
|
|
| 22 |
|
| 23 |
-
- **Base
|
| 24 |
-
- **
|
| 25 |
-
- **Evaluation:** 150 pinned holdout
|
| 26 |
-
- **Deployment:** INT8 ONNX
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
## Results
|
| 29 |
|
| 30 |
-
|
| 31 |
|
| 32 |
| Metric | Stock NLI | POPIA fine-tune | Delta |
|
| 33 |
|-------------------------------------|-----------|-----------------|-------------|
|
| 34 |
-
| Macro F1
|
| 35 |
| Accuracy | 0.707 | **0.833** | +0.127 |
|
| 36 |
| POPIA consent | 0.612 | 0.902 | +0.290 |
|
| 37 |
| POPIA minimality | 0.646 | 0.753 | +0.106 |
|
|
@@ -41,21 +49,9 @@ Evaluated on the pinned `eval.jsonl` (150 pairs) against the stock base model as
|
|
| 41 |
| POPIA general processing | 0.400 | 0.893 | +0.493 |
|
| 42 |
| POPIA data subject rights | 0.400 | 0.883 | +0.483 |
|
| 43 |
|
| 44 |
-
|
| 45 |
|
| 46 |
-
##
|
| 47 |
-
|
| 48 |
-
Label order matches the base model's `config.id2label`:
|
| 49 |
-
|
| 50 |
-
| Index | Label |
|
| 51 |
-
|-------|----------------|
|
| 52 |
-
| 0 | contradiction |
|
| 53 |
-
| 1 | entailment |
|
| 54 |
-
| 2 | neutral |
|
| 55 |
-
|
| 56 |
-
The `POPIAJudge` reads `probs[1]` (entailment) as the "compliance matches intent" score.
|
| 57 |
-
|
| 58 |
-
## Usage
|
| 59 |
|
| 60 |
```bash
|
| 61 |
pip install 'semantix-ai[popia]'
|
|
@@ -72,8 +68,49 @@ verdict = judge.evaluate(
|
|
| 72 |
threshold=POPIAJudge.recommended_threshold, # 0.75
|
| 73 |
)
|
| 74 |
print(verdict.passed, verdict.score)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
```
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
## Files
|
| 78 |
|
| 79 |
| Path | Purpose |
|
|
@@ -84,23 +121,67 @@ print(verdict.passed, verdict.score)
|
|
| 84 |
| `onnx/model_qint8_arm64.onnx` | INT8 quantized, ARM64 (Apple Silicon, Graviton) |
|
| 85 |
| `tokenizer.json` | Rust `tokenizers` fast-tokenizer config |
|
| 86 |
| `config.json` | Model config (shared across variants) |
|
| 87 |
-
| `eval.jsonl` | Pinned 150-pair holdout
|
| 88 |
|
| 89 |
-
|
| 90 |
|
| 91 |
## Training
|
| 92 |
|
| 93 |
- 5 epochs, batch size 8, lr 2e-5, warmup 10%, weight decay 0.01
|
| 94 |
-
- Cross-entropy loss, early stopping on `eval_loss` against
|
| 95 |
- CPU training on 180 rows: ~6 min
|
| 96 |
-
- Reproducible via `scripts/train_popia.py`
|
| 97 |
|
| 98 |
## Intended use
|
| 99 |
|
| 100 |
-
Compliance-sensitive audit of LLM outputs where a deterministic,
|
| 101 |
-
non-API-dependent judge is required.
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
## License
|
| 105 |
|
| 106 |
-
Apache 2.0.
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
license: apache-2.0
|
| 4 |
library_name: transformers
|
| 5 |
base_model: cross-encoder/nli-MiniLM2-L6-H768
|
| 6 |
+
datasets:
|
| 7 |
+
- labrat-aiko/popia-compliance-nli
|
| 8 |
tags:
|
| 9 |
- nli
|
| 10 |
- zero-shot-classification
|
| 11 |
- compliance
|
| 12 |
- popia
|
| 13 |
- south-africa
|
| 14 |
+
- privacy
|
| 15 |
+
- legal
|
| 16 |
- onnx
|
| 17 |
- quantized
|
| 18 |
+
- cpu
|
| 19 |
pipeline_tag: zero-shot-classification
|
| 20 |
---
|
| 21 |
|
| 22 |
# nli-popia-v1
|
| 23 |
|
| 24 |
+
Cross-encoder NLI model fine-tuned for deterministic, local, auditable
|
| 25 |
+
validation of LLM outputs against **POPIA** (South Africa's Protection of
|
| 26 |
+
Personal Information Act, 2013) compliance clauses. Drop-in judge for the
|
| 27 |
+
`POPIAJudge` in [semantix-ai](https://github.com/labrat-akhona/semantix-ai).
|
| 28 |
|
| 29 |
+
- **Base:** `cross-encoder/nli-MiniLM2-L6-H768` (~22M params)
|
| 30 |
+
- **Training data:** 180 hand-authored triples → [`labrat-aiko/popia-compliance-nli`](https://huggingface.co/datasets/labrat-aiko/popia-compliance-nli)
|
| 31 |
+
- **Evaluation:** 150-pair pinned holdout, hash-verified by release gate
|
| 32 |
+
- **Deployment:** 4 INT8 ONNX CPU variants (AVX2 / AVX512 / AVX512-VNNI / ARM64), auto-selected
|
| 33 |
+
- **Inference:** ~70 ms mean on CPU, $0 API cost, no internet required
|
| 34 |
+
- **Release gate:** macro F1 delta ≥ 0.10 AND no per-clause regression — enforced in CI on every push to master
|
| 35 |
|
| 36 |
+
## Results (150-pair pinned holdout)
|
| 37 |
|
| 38 |
+
Fine-tuned checkpoint versus the stock base model:
|
| 39 |
|
| 40 |
| Metric | Stock NLI | POPIA fine-tune | Delta |
|
| 41 |
|-------------------------------------|-----------|-----------------|-------------|
|
| 42 |
+
| **Macro F1** | 0.517 | **0.813** | **+0.296** |
|
| 43 |
| Accuracy | 0.707 | **0.833** | +0.127 |
|
| 44 |
| POPIA consent | 0.612 | 0.902 | +0.290 |
|
| 45 |
| POPIA minimality | 0.646 | 0.753 | +0.106 |
|
|
|
|
| 49 |
| POPIA general processing | 0.400 | 0.893 | +0.493 |
|
| 50 |
| POPIA data subject rights | 0.400 | 0.883 | +0.483 |
|
| 51 |
|
| 52 |
+
Every clause improved. No regressions. Release gate: **PASS**.
|
| 53 |
|
| 54 |
+
## Quick start
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
```bash
|
| 57 |
pip install 'semantix-ai[popia]'
|
|
|
|
| 68 |
threshold=POPIAJudge.recommended_threshold, # 0.75
|
| 69 |
)
|
| 70 |
print(verdict.passed, verdict.score)
|
| 71 |
+
# False 0.031
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
Using the raw ONNX directly (no PyTorch, no semantix dependency):
|
| 75 |
+
|
| 76 |
+
```python
|
| 77 |
+
import numpy as np, onnxruntime as ort
|
| 78 |
+
from tokenizers import Tokenizer
|
| 79 |
+
from huggingface_hub import hf_hub_download
|
| 80 |
+
|
| 81 |
+
model_path = hf_hub_download("labrat-aiko/nli-popia-v1", "onnx/model_quint8_avx2.onnx")
|
| 82 |
+
tok_path = hf_hub_download("labrat-aiko/nli-popia-v1", "tokenizer.json")
|
| 83 |
+
session = ort.InferenceSession(model_path)
|
| 84 |
+
tok = Tokenizer.from_file(tok_path)
|
| 85 |
+
|
| 86 |
+
encoded = tok.encode(
|
| 87 |
+
"Your data will be used to generate marketing offers without opt-in.",
|
| 88 |
+
"The responsible party is obtaining explicit opt-in consent.",
|
| 89 |
+
)
|
| 90 |
+
logits = session.run(None, {
|
| 91 |
+
"input_ids": np.array([encoded.ids], dtype=np.int64),
|
| 92 |
+
"attention_mask": np.array([encoded.attention_mask], dtype=np.int64),
|
| 93 |
+
})[0][0]
|
| 94 |
+
probs = np.exp(logits) / np.exp(logits).sum()
|
| 95 |
+
# Label order (id2label): {0: contradiction, 1: entailment, 2: neutral}
|
| 96 |
+
print("entailment:", probs[1])
|
| 97 |
```
|
| 98 |
|
| 99 |
+
## Label order (read this before using the scores)
|
| 100 |
+
|
| 101 |
+
Label indices match the base model's `config.id2label`:
|
| 102 |
+
|
| 103 |
+
| Index | Label |
|
| 104 |
+
|-------|----------------|
|
| 105 |
+
| 0 | contradiction |
|
| 106 |
+
| 1 | **entailment** |
|
| 107 |
+
| 2 | neutral |
|
| 108 |
+
|
| 109 |
+
`POPIAJudge` (semantix-ai ≥ 0.2.0) reads `probs[1]` as the compliance score.
|
| 110 |
+
A Python-side bug in v0.1.5–v0.1.13 of semantix-ai was reading `probs[2]`
|
| 111 |
+
(neutral) — fixed in v0.2.0. The ONNX artefacts in this repo are unchanged;
|
| 112 |
+
only downstream Python code needed the label index correction.
|
| 113 |
+
|
| 114 |
## Files
|
| 115 |
|
| 116 |
| Path | Purpose |
|
|
|
|
| 121 |
| `onnx/model_qint8_arm64.onnx` | INT8 quantized, ARM64 (Apple Silicon, Graviton) |
|
| 122 |
| `tokenizer.json` | Rust `tokenizers` fast-tokenizer config |
|
| 123 |
| `config.json` | Model config (shared across variants) |
|
| 124 |
+
| `eval.jsonl` | Pinned 150-pair holdout (bundled for reproducibility) |
|
| 125 |
|
| 126 |
+
`POPIAJudge` auto-detects the best variant at load time; you never pick manually.
|
| 127 |
|
| 128 |
## Training
|
| 129 |
|
| 130 |
- 5 epochs, batch size 8, lr 2e-5, warmup 10%, weight decay 0.01
|
| 131 |
+
- Cross-entropy loss, early stopping on `eval_loss` against 10% dev split
|
| 132 |
- CPU training on 180 rows: ~6 min
|
| 133 |
+
- Reproducible via [`scripts/train_popia.py`](https://github.com/labrat-akhona/semantix-ai/blob/master/scripts/train_popia.py)
|
| 134 |
|
| 135 |
## Intended use
|
| 136 |
|
| 137 |
+
- Compliance-sensitive audit of LLM outputs where a deterministic,
|
| 138 |
+
auditable, non-API-dependent judge is required (e.g. CI validation,
|
| 139 |
+
audit trails, POPIA-aware `@validate_intent` decorators, MCP tools).
|
| 140 |
+
- Per-clause threshold-tunable gating: each of the 7 POPIA clauses has
|
| 141 |
+
a pre-tuned F1-optimal threshold shipped in
|
| 142 |
+
`semantix.presets.popia` — you can override per-deployment.
|
| 143 |
+
- Educational / research benchmark for local NLI approaches to
|
| 144 |
+
compliance judging versus LLM-as-judge pipelines.
|
| 145 |
+
|
| 146 |
+
## Limitations
|
| 147 |
+
|
| 148 |
+
- **Does not replace a POPIA specialist, a DPIA, or the Information
|
| 149 |
+
Regulator.** Flags semantic mismatch between text and a clause intent;
|
| 150 |
+
not a legal opinion.
|
| 151 |
+
- **English-only.** No Afrikaans / isiZulu / Sesotho coverage yet.
|
| 152 |
+
- **Narrow domain.** 7 canonical clauses out of POPIA's full surface; some
|
| 153 |
+
adjacent areas (direct marketing under §69, automated decision-making
|
| 154 |
+
under §71) are not separately modelled.
|
| 155 |
+
- **Hand-authored eval set** (same author as train). External validation
|
| 156 |
+
is welcome and the eval set is Apache 2.0 so you can add your own cases.
|
| 157 |
+
- **Threshold-sensitive.** Per-clause F1-optimal thresholds are tuned on
|
| 158 |
+
*this* holdout. If your domain's distribution differs (dialect,
|
| 159 |
+
adversarial inputs, industry jargon), re-tune on your own held-out
|
| 160 |
+
set — the full recipe is in the semantix-ai repo.
|
| 161 |
+
|
| 162 |
+
## Ecosystem
|
| 163 |
+
|
| 164 |
+
- **Training dataset**: [`labrat-aiko/popia-compliance-nli`](https://huggingface.co/datasets/labrat-aiko/popia-compliance-nli) (Apache 2.0)
|
| 165 |
+
- **Library**: [`semantix-ai`](https://pypi.org/project/semantix-ai/) (MIT), ships `POPIAJudge` + 7 presets
|
| 166 |
+
- **Source**: [labrat-akhona/semantix-ai](https://github.com/labrat-akhona/semantix-ai)
|
| 167 |
+
- **Writeup**: [I Fine-Tuned a Compliance Judge and Beat the Stock Model by +29.6pp F1](https://github.com/labrat-akhona/semantix-ai/blob/master/articles/devto-popia-finetune.md)
|
| 168 |
+
|
| 169 |
+
## Citation
|
| 170 |
+
|
| 171 |
+
```bibtex
|
| 172 |
+
@misc{nli-popia-v1-2026,
|
| 173 |
+
author = {Eland, Akhona},
|
| 174 |
+
title = {nli-popia-v1: A local NLI cross-encoder fine-tuned for POPIA compliance validation},
|
| 175 |
+
year = 2026,
|
| 176 |
+
publisher = {Hugging Face},
|
| 177 |
+
howpublished = {\url{https://huggingface.co/labrat-aiko/nli-popia-v1}},
|
| 178 |
+
note = {Apache 2.0}
|
| 179 |
+
}
|
| 180 |
+
```
|
| 181 |
|
| 182 |
## License
|
| 183 |
|
| 184 |
+
Apache 2.0. Commercial and derivative use explicitly permitted. Forking
|
| 185 |
+
to build sibling compliance models (GDPR, HIPAA, EU AI Act, UK DPA) is
|
| 186 |
+
actively encouraged — same recipe, different clause corpus. A link back is
|
| 187 |
+
appreciated but not required.
|