bitz support commited on
Commit
f1ee6d0
·
1 Parent(s): e1a3efb

gradio app for masked modeling task

Browse files
Files changed (4) hide show
  1. README.md +68 -14
  2. app.py +192 -0
  3. requirements.txt +6 -0
  4. runtime.txt +1 -0
README.md CHANGED
@@ -1,14 +1,68 @@
1
- ---
2
- title: Masked Language Modeling
3
- emoji: 🐢
4
- colorFrom: yellow
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 6.18.0
8
- python_version: '3.13'
9
- app_file: app.py
10
- pinned: false
11
- short_description: 'AfriBERT (Kenya adapted) Masked Language Modeling '
12
- ---
13
-
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AfriBERT Kenya Masked LM Gradio App
2
+
3
+ Gradio demo for comparing masked-language-modeling predictions from:
4
+
5
+ - Base model: `castorini/afriberta_large`
6
+ - Adapted model: `Rogendo/afribert-kenya-adapted`
7
+
8
+ The app uses the same tokenizer, `castorini/afriberta_large`, for both models so the MLM predictions are directly comparable.
9
+
10
+ The app supports Swahili, Sheng, Kenyan institutional text, M-PESA language, and English-Swahili code-switching examples.
11
+
12
+ ## Run locally
13
+
14
+ PyTorch does not currently install on Python 3.14. Use Python 3.10 for this app.
15
+
16
+ ```bash
17
+ cd /Users/bitzsupport/Desktop/Portfoliio/afribert-kenya-mlm-gradio
18
+ python3.10 -m venv venv
19
+ source venv/bin/activate
20
+ python -m pip install --upgrade pip
21
+ pip install -r requirements.txt
22
+ export HF_TOKEN="your_huggingface_read_token"
23
+ python app.py
24
+ ```
25
+
26
+ If `python3.10` is not installed on macOS:
27
+
28
+ ```bash
29
+ brew install python@3.10
30
+ ```
31
+
32
+ If the model is public, `HF_TOKEN` is optional. If it is private, the token must have read access.
33
+
34
+ Optional overrides:
35
+
36
+ ```bash
37
+ export MODEL_ID="Rogendo/afribert-kenya-adapted"
38
+ export ADAPTED_MODEL_ID="Rogendo/afribert-kenya-adapted"
39
+ export BASE_MODEL_ID="castorini/afriberta_large"
40
+ export TOKENIZER_ID="castorini/afriberta_large"
41
+ ```
42
+
43
+ ## Hugging Face Space
44
+
45
+ Create a Gradio Space and upload:
46
+
47
+ - `app.py`
48
+ - `requirements.txt`
49
+ - `README.md`
50
+ - `runtime.txt`
51
+
52
+ Then add a Space secret named `HF_TOKEN` with a Hugging Face token that can read the model.
53
+
54
+ ## Usage
55
+
56
+ Use the tokenizer mask token shown in the app: `<mask>`. `[MASK]` is also accepted and automatically converted.
57
+
58
+ Examples:
59
+
60
+ ```text
61
+ Tulifanya meeting jana na manager akasema <mask> itakuwa ready wiki ijayo.
62
+ ```
63
+
64
+ ```text
65
+ Msee alikuwa poa sana, akanisaidia kupata <mask> ya ofisi.
66
+ ```
67
+
68
+ The first output table compares the base and adapted model rank-by-rank. The second table shows each model's completed sentence for every prediction.
app.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Any
3
+
4
+ import gradio as gr
5
+ import torch
6
+ from transformers import AutoModelForMaskedLM, AutoTokenizer
7
+
8
+
9
+ ADAPTED_MODEL_ID = os.getenv("ADAPTED_MODEL_ID", os.getenv("MODEL_ID", "Rogendo/afribert-kenya-adapted"))
10
+ BASE_MODEL_ID = os.getenv("BASE_MODEL_ID", "castorini/afriberta_large")
11
+ TOKENIZER_ID = os.getenv("TOKENIZER_ID", "castorini/afriberta_large")
12
+ HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN")
13
+
14
+
15
+ def load_models() -> tuple[Any, Any, Any, torch.device]:
16
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
17
+ tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_ID, token=HF_TOKEN, use_fast=False)
18
+ base_model = AutoModelForMaskedLM.from_pretrained(BASE_MODEL_ID, use_safetensors=True)
19
+ adapted_model = AutoModelForMaskedLM.from_pretrained(
20
+ ADAPTED_MODEL_ID,
21
+ token=HF_TOKEN,
22
+ use_safetensors=True,
23
+ )
24
+
25
+ base_model.to(device)
26
+ adapted_model.to(device)
27
+ base_model.eval()
28
+ adapted_model.eval()
29
+ return tokenizer, base_model, adapted_model, device
30
+
31
+
32
+ tokenizer, base_model, adapted_model, device = load_models()
33
+ MASK_TOKEN = tokenizer.mask_token or "[MASK]"
34
+
35
+
36
+ EXAMPLES = [
37
+ f"Oya, twendeni zetu, kuna {MASK_TOKEN} flani ameniudhi.",
38
+ f"Tuma {MASK_TOKEN} kwa kutumia nambari ya simu kupitia huduma ya M-PESA.",
39
+ f"Mtoto aliripotiwa kwa ofisi ya {MASK_TOKEN} wa jamii baada ya kudhulumiwa nyumbani.",
40
+ f"Tulifanya meeting jana na manager akasema {MASK_TOKEN} itakuwa ready wiki ijayo.",
41
+ f"Msee alikuwa poa sana, akanisaidia kupata {MASK_TOKEN} ya ofisi.",
42
+ ]
43
+
44
+
45
+ def normalize_input(text: str) -> str:
46
+ text = (text or "").strip()
47
+ if "[MASK]" in text and MASK_TOKEN != "[MASK]":
48
+ text = text.replace("[MASK]", MASK_TOKEN)
49
+ return text
50
+
51
+
52
+ def model_predictions(model, inputs, mask_positions, top_k: int, model_label: str) -> list[list[Any]]:
53
+ with torch.no_grad():
54
+ outputs = model(**inputs)
55
+ logits = outputs.logits[0]
56
+
57
+ rows = []
58
+ for mask_index, position in enumerate(mask_positions.tolist(), start=1):
59
+ probabilities = torch.softmax(logits[position], dim=-1)
60
+ scores, token_ids = torch.topk(probabilities, k=int(top_k))
61
+
62
+ for rank, (score, token_id) in enumerate(zip(scores, token_ids), start=1):
63
+ token = tokenizer.decode([token_id.item()]).strip()
64
+ completed = inputs["input_ids"][0].clone()
65
+ completed[position] = token_id
66
+ sequence = tokenizer.decode(completed, skip_special_tokens=True)
67
+
68
+ rows.append([
69
+ model_label,
70
+ mask_index,
71
+ rank,
72
+ token,
73
+ round(float(score.item()), 4),
74
+ sequence,
75
+ ])
76
+
77
+ return rows
78
+
79
+
80
+ def predict_masks(text: str, top_k: int) -> tuple[str, list[list[Any]], list[list[Any]]]:
81
+ text = normalize_input(text)
82
+
83
+ if not text:
84
+ return "Enter a sentence with a mask token.", [], []
85
+
86
+ if MASK_TOKEN not in text:
87
+ return f"Add at least one mask token: `{MASK_TOKEN}`", [], []
88
+
89
+ inputs = tokenizer(text, return_tensors="pt").to(device)
90
+ mask_positions = (inputs["input_ids"][0] == tokenizer.mask_token_id).nonzero(as_tuple=True)[0]
91
+
92
+ if len(mask_positions) == 0:
93
+ return f"No valid mask token found. Use `{MASK_TOKEN}`.", [], []
94
+
95
+ base_rows = model_predictions(base_model, inputs, mask_positions, top_k, "Base AfriBERT")
96
+ adapted_rows = model_predictions(adapted_model, inputs, mask_positions, top_k, "Adapted AfriBERT Kenya")
97
+ comparison_rows = []
98
+
99
+ for base_row, adapted_row in zip(base_rows, adapted_rows):
100
+ comparison_rows.append([
101
+ base_row[1],
102
+ base_row[2],
103
+ base_row[3],
104
+ base_row[4],
105
+ adapted_row[3],
106
+ adapted_row[4],
107
+ ])
108
+
109
+ summary = (
110
+ f"Base model: `{BASE_MODEL_ID}`\n\n"
111
+ f"Adapted model: `{ADAPTED_MODEL_ID}`\n\n"
112
+ f"Tokenizer: `{TOKENIZER_ID}`\n\n"
113
+ f"Mask token: `{MASK_TOKEN}`\n\n"
114
+ f"Found {len(mask_positions)} mask position{'s' if len(mask_positions) != 1 else ''}."
115
+ )
116
+ return summary, comparison_rows, base_rows + adapted_rows
117
+
118
+
119
+ with gr.Blocks(title="AfriBERT Kenya Masked LM") as demo:
120
+ gr.Markdown(
121
+ """
122
+ # AfriBERT Kenya Masked Language Modeling
123
+
124
+ Compare base AfriBERT against the Kenya-adapted model on Swahili, Sheng,
125
+ Kenyan institutional text, M-PESA language, and English-Swahili code-switching.
126
+ """
127
+ )
128
+
129
+ with gr.Row():
130
+ with gr.Column(scale=2):
131
+ text_input = gr.Textbox(
132
+ label="Input text",
133
+ value=EXAMPLES[0],
134
+ lines=4,
135
+ placeholder=f"Type a sentence containing {MASK_TOKEN}",
136
+ )
137
+ top_k = gr.Slider(
138
+ label="Top predictions",
139
+ minimum=1,
140
+ maximum=10,
141
+ value=5,
142
+ step=1,
143
+ )
144
+ predict_button = gr.Button("Compare masked-token predictions", variant="primary")
145
+
146
+ with gr.Column(scale=1):
147
+ gr.Markdown(
148
+ f"""
149
+ **How to use**
150
+
151
+ Add `{MASK_TOKEN}` where you want the model to predict a token.
152
+ `[MASK]` is also accepted and converted automatically.
153
+
154
+ For private models, set `HF_TOKEN` before launching the app.
155
+ The same base AfriBERT tokenizer is used for both models.
156
+ """
157
+ )
158
+
159
+ summary_output = gr.Markdown()
160
+ comparison_output = gr.Dataframe(
161
+ headers=["Mask", "Rank", "Base prediction", "Base score", "Adapted prediction", "Adapted score"],
162
+ datatype=["number", "number", "str", "number", "str", "number"],
163
+ label="Side-by-side comparison",
164
+ wrap=True,
165
+ )
166
+ details_output = gr.Dataframe(
167
+ headers=["Model", "Mask", "Rank", "Prediction", "Score", "Completed sentence"],
168
+ datatype=["str", "number", "number", "str", "number", "str"],
169
+ label="Detailed predictions",
170
+ wrap=True,
171
+ )
172
+
173
+ gr.Examples(
174
+ examples=EXAMPLES,
175
+ inputs=text_input,
176
+ )
177
+
178
+ predict_button.click(
179
+ fn=predict_masks,
180
+ inputs=[text_input, top_k],
181
+ outputs=[summary_output, comparison_output, details_output],
182
+ )
183
+
184
+ text_input.submit(
185
+ fn=predict_masks,
186
+ inputs=[text_input, top_k],
187
+ outputs=[summary_output, comparison_output, details_output],
188
+ )
189
+
190
+
191
+ if __name__ == "__main__":
192
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio>=4.44.0,<6
2
+ numpy<2
3
+ torch>=2.2.0
4
+ transformers>=4.44.0,<5
5
+ sentencepiece==0.1.99
6
+ protobuf==3.20.3
runtime.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python-3.10.13