Prosperitytech commited on
Commit
dbfd080
·
verified ·
1 Parent(s): 5d642a1

Lighthouse Keeper 0.1

Browse files
Files changed (7) hide show
  1. .gitattributes +1 -0
  2. README.md +144 -0
  3. config.json +45 -0
  4. example.py +36 -0
  5. keeper-fp32.onnx +3 -0
  6. keeper.onnx +3 -0
  7. keeper.spm +3 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ keeper.spm filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ - pcm
6
+ - yo
7
+ - ha
8
+ - ig
9
+ library_name: onnx
10
+ pipeline_tag: text-classification
11
+ tags:
12
+ - child-safety
13
+ - content-moderation
14
+ - nigeria
15
+ - on-device
16
+ ---
17
+
18
+ # Lighthouse Keeper 0.1
19
+
20
+ Keeper is the text safety model inside Lighthouse, a child safety app from Prosperity Tech. It reads short messages on a child's phone and scores them for 13 kinds of risk. It is small enough to run on a cheap Android phone with no internet connection, so the message never has to leave the device.
21
+
22
+ We built it for Nigerian children. It understands Nigerian English, Pidgin, Yoruba, Hausa and Igbo, including the way teenagers actually type: mixed languages, slang, no tone marks, typos.
23
+
24
+ This is our first version. It is useful, it is not finished, and the weak spots are listed below.
25
+
26
+ ## What it does
27
+
28
+ Give it one message. It returns a score for each of these 13 categories:
29
+
30
+ bullying, eating disorders, gambling, graphic content, grooming, hate speech, privacy threats, scams, self harm, sextortion, sexual content, substance use, violence.
31
+
32
+ For each category you get four numbers that add up to 1: how likely the message is at level none, low, review or high.
33
+
34
+ - **high**: act now. A stranger asking a child to keep a secret and send photos.
35
+ - **review**: a parent should look. A friend pushing a betting app.
36
+ - **low**: worth counting, not worth an alert. A mild insult between classmates.
37
+ - **none**: nothing there, even if the message uses the same words.
38
+
39
+ In the Lighthouse app we take the category with the highest level and only flag it when the chance of any risk is at least 0.8. When two categories tie, the more serious one wins, so a threat to share nudes shows as sextortion and not bullying. The order is in `config.json`. You can pick your own cutoff.
40
+
41
+ ## Size and speed
42
+
43
+ | | |
44
+ |---|---|
45
+ | Parameters | 8.3 million |
46
+ | `keeper.onnx` (int8, what we ship) | 8.5 MB |
47
+ | `keeper-fp32.onnx` | 33 MB |
48
+ | `keeper.spm` (word pieces) | 0.6 MB |
49
+ | Longest input | 128 word pieces, longer messages are cut |
50
+ | Speed | under 1 ms per message on a laptop CPU |
51
+
52
+ The model is a 4 layer encoder, 256 wide, with its own 20,000 piece SentencePiece vocabulary. It takes any length up to 128.
53
+
54
+ ## How to use it
55
+
56
+ ```bash
57
+ pip install onnxruntime sentencepiece huggingface_hub numpy
58
+ python example.py "how far, wetin be your house address and which school you dey"
59
+ ```
60
+
61
+ `example.py` in this repo shows the whole thing in about 30 lines: split the message with `keeper.spm`, put id 2 in front, run `keeper.onnx`, read back 13 rows of 4 numbers. The category order and level names are in `config.json`.
62
+
63
+ ## How we built it
64
+
65
+ We did not have a big labelled dataset for Nigerian languages, so we made one.
66
+
67
+ 1. We wrote a short rule sheet for each category: what counts, what doesn't, and examples of each level. The rules come from public child safety guidance and Nigerian hate speech research.
68
+ 2. We collected about 300,000 messages from public datasets and had gpt-oss-120b write about 100,000 more in Nigerian English and Pidgin.
69
+ 3. A large open safety model, [gpt-oss-safeguard-20b](https://huggingface.co/openai/gpt-oss-safeguard-20b), read every message against every rule sheet and gave a score from 0 to 3. We kept its full probabilities, not just its answer.
70
+ 4. Keeper learned to copy those scores. This is called distillation: the big model teaches, the small one learns.
71
+ 5. The first versions were weak on Yoruba and Hausa. So we had [AfriqueQwen 9B](https://huggingface.co/McGill-NLP/AfriqueQwen3.5-9B-50Langs-Instruct-v1) write 68,000 new messages in Yoruba, Hausa and Igbo, each with an English meaning. The teacher scored the English meaning and Keeper learned that score for the original message. We also let human labels from AfriHate win over the teacher, and we read 2.4 million lines of Yoruba, Hausa, Igbo, Pidgin and English web text before training.
72
+
73
+ The final training set is about 460,000 messages. Training takes under an hour on one GPU.
74
+
75
+ ## How well it works
76
+
77
+ We test on 746 messages that Keeper never saw in training: real AfriHate tweets, messages from OpenAI's teen safety test pack, and messages we wrote across all five languages and all 13 categories. A message passes when Keeper puts it at the right level for its category.
78
+
79
+ | | Keeper 0.1 |
80
+ |---|---|
81
+ | All 746 messages | 80% |
82
+ | Risky messages caught | 84% |
83
+ | Safe messages flagged by mistake | 12% |
84
+ | Pidgin | 87% |
85
+ | Igbo | 81% |
86
+ | Yoruba | 78% |
87
+ | English | 76% |
88
+ | Hausa | 74% |
89
+ | Real AfriHate tweets | 74% |
90
+
91
+ For comparison, the phrase word list that Lighthouse used before scores 37% on the same test and catches 22% of risky messages. The teacher model itself scores 85%.
92
+
93
+ The 557 messages we wrote are published as a test set: [Prosperitytech/keeper-nigerian-safety-test](https://huggingface.co/datasets/Prosperitytech/keeper-nigerian-safety-test). Keeper gets 84% on that part. The tweets and the OpenAI pack are not ours to republish.
94
+
95
+ ## Where it falls short
96
+
97
+ - **False alarms.** Look across all 13 categories and Keeper flags something in about 28% of ordinary safe messages. Most of those are at level low. With a 0.8 cutoff that drops to about 14%, and it then catches about 77% of what the teacher calls risky. Pick the cutoff for your use.
98
+ - **It reacts to the style of AI-written text.** Many of the new Yoruba, Hausa and Igbo examples were written by a model, and Keeper partly learned that style as a risk signal. Real Yoruba and Hausa chat may score lower than our test suggests. Fixing this is the main goal for the next version.
99
+ - **The test is small.** About 100 messages per African language. A single score can move by 5 to 10 points from luck.
100
+ - **Hausa and Igbo are behind Pidgin and English.**
101
+ - **Text only.** It does not look at images, voice notes or links.
102
+ - **One message at a time.** Grooming and bullying often only show up across a whole conversation.
103
+
104
+ Keeper is meant to help a parent notice something. It should not make decisions about a child on its own.
105
+
106
+ ## Data we trained on
107
+
108
+ All of it allows commercial use. Thank you to everyone who made these public.
109
+
110
+ | Dataset | License |
111
+ |---|---|
112
+ | [AfriHate](https://huggingface.co/datasets/afrihate/afrihate) | Apache 2.0 |
113
+ | [HausaHate](https://huggingface.co/datasets/franciellevargas/HausaHate) | Apache 2.0 |
114
+ | [BRIGHTER emotion](https://huggingface.co/datasets/brighter-dataset/BRIGHTER-emotion-categories) | CC BY 4.0 |
115
+ | [Nigerian hate speech (Mendeley)](https://data.mendeley.com/datasets/r5jsynhxsx/1) | CC BY 4.0 |
116
+ | [Nigerian Twitter cyberbullying (Mendeley)](https://data.mendeley.com/datasets/5d3y8v68sd/1) | CC BY 4.0 |
117
+ | [Hausa English code switched (Mendeley)](https://data.mendeley.com/datasets/3xjyjsf4sb/1) | CC BY 4.0 |
118
+ | [SMS phishing (Mendeley)](https://data.mendeley.com/datasets/f45bkkt8pr) | CC BY 4.0 |
119
+ | [NaijaScamBench](https://huggingface.co/datasets/Eterniti/NaijaScamBench) | CC BY 4.0 |
120
+ | [Africa smishing SMS](https://huggingface.co/datasets/electricsheepafrica/africa-smishing-sms-phishing) | MIT |
121
+ | [NaijaWeb](https://huggingface.co/datasets/saheedniyi/naijaweb) | MIT |
122
+ | [FineWeb 2](https://huggingface.co/datasets/HuggingFaceFW/fineweb-2) | ODC-BY |
123
+ | [Civil Comments](https://huggingface.co/datasets/google/civil_comments) | CC0 |
124
+ | [Measuring Hate Speech](https://huggingface.co/datasets/ucberkeley-dlab/measuring-hate-speech) | CC BY 4.0 |
125
+ | [Aegis 2.0](https://huggingface.co/datasets/nvidia/Aegis-AI-Content-Safety-Dataset-2.0) | CC BY 4.0 |
126
+ | [Nemotron Safety Guard v3](https://huggingface.co/datasets/nvidia/Nemotron-Safety-Guard-Dataset-v3) | CC BY 4.0 |
127
+ | [WildGuardMix](https://huggingface.co/datasets/allenai/wildguardmix) | ODC-BY |
128
+ | [XGuard Train Open](https://huggingface.co/datasets/Alibaba-AAIG/XGuard-Train-Open-200K) | Apache 2.0 |
129
+ | [Aya red teaming](https://huggingface.co/datasets/CohereLabs/aya_redteaming) | Apache 2.0 |
130
+ | [OpenPII](https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m) | CC BY 4.0 |
131
+ | [Scam dialogue](https://huggingface.co/datasets/BothBosu/scam-dialogue) | Apache 2.0 |
132
+ | [SMS Spam Collection](https://archive.ics.uci.edu/dataset/228) | CC BY 4.0 |
133
+
134
+ Models used to build it: gpt-oss-safeguard-20b and gpt-oss-120b from OpenAI (Apache 2.0), and AfriqueQwen 9B from McGill NLP (CC BY 4.0).
135
+
136
+ We are not publishing the training set itself. Parts of it are written to look like grooming and sextortion, and we don't want that text floating around on its own.
137
+
138
+ ## License
139
+
140
+ Apache 2.0. You can use Keeper in your own products, paid ones included. Please credit Prosperity Tech and link back here.
141
+
142
+ ## Contact
143
+
144
+ Prosperity Tech, makers of Lighthouse. Open an issue on this page or reach us through [lighthouse](https://github.com/prosperitytechoss/lighthouse) on GitHub. If Keeper gets something wrong in your language, we want to hear about it.
config.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "lighthouse-keeper",
3
+ "version": "0.1",
4
+ "parameters": 8330000,
5
+ "max_tokens": 128,
6
+ "bos_id": 2,
7
+ "pad_id": 0,
8
+ "categories": [
9
+ "bullying",
10
+ "eating-disorders",
11
+ "gambling",
12
+ "graphic-content",
13
+ "grooming",
14
+ "hate-speech",
15
+ "privacy-threats",
16
+ "scams",
17
+ "self-harm",
18
+ "sextortion",
19
+ "sexual-content",
20
+ "substance-use",
21
+ "violence"
22
+ ],
23
+ "levels": [
24
+ "none",
25
+ "low",
26
+ "review",
27
+ "high"
28
+ ],
29
+ "suggested_min_risk": 0.8,
30
+ "tie_break_order": [
31
+ "self-harm",
32
+ "sextortion",
33
+ "grooming",
34
+ "sexual-content",
35
+ "violence",
36
+ "graphic-content",
37
+ "eating-disorders",
38
+ "substance-use",
39
+ "hate-speech",
40
+ "scams",
41
+ "privacy-threats",
42
+ "gambling",
43
+ "bullying"
44
+ ]
45
+ }
example.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import sys
3
+
4
+ import numpy as np
5
+ import onnxruntime as ort
6
+ import sentencepiece as spm
7
+ from huggingface_hub import hf_hub_download
8
+
9
+ REPO = "Prosperitytech/lighthouse-keeper"
10
+
11
+ cfg = json.load(open(hf_hub_download(REPO, "config.json")))
12
+ sp = spm.SentencePieceProcessor(model_file=hf_hub_download(REPO, "keeper.spm"))
13
+ session = ort.InferenceSession(hf_hub_download(REPO, "keeper.onnx"), providers=["CPUExecutionProvider"])
14
+
15
+
16
+ def score(text):
17
+ ids = [cfg["bos_id"]] + sp.encode(text)[: cfg["max_tokens"] - 1]
18
+ probs = session.run(None, {"input_ids": np.array([ids], dtype=np.int64)})[0][0]
19
+ return {cat: dict(zip(cfg["levels"], map(float, p))) for cat, p in zip(cfg["categories"], probs)}
20
+
21
+
22
+ def flag(text, min_risk=cfg["suggested_min_risk"]):
23
+ priority = cfg["tie_break_order"]
24
+ best = None
25
+ for cat, p in score(text).items():
26
+ level = max(p, key=p.get)
27
+ risk = 1 - p["none"]
28
+ if level != "none" and risk >= min_risk:
29
+ key = (cfg["levels"].index(level), p[level] + 0.06 * (1 - priority.index(cat) / len(priority)))
30
+ if best is None or key > best[1]:
31
+ best = (cat, key, p[level])
32
+ return None if best is None else {"category": best[0], "level": cfg["levels"][best[1][0]], "confidence": round(best[2], 3)}
33
+
34
+
35
+ if __name__ == "__main__":
36
+ print(flag(" ".join(sys.argv[1:]) or "send me your BVN make I help you collect the money"))
keeper-fp32.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aaddc7c67d9eb09eaf4a442605717c9d4e96c8a0caf93c44856b20f2caf57c0a
3
+ size 33357151
keeper.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f290ae65968cd380ce09e075ee2931d0a0230cd007d3493492bbf4a1ffc9d78f
3
+ size 8465401
keeper.spm ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b9becb67f3537c7cea9674216493f0b2de1916c2007382b9d2c8e5a46b22c41e
3
+ size 588662