Skip to content

Commit bd48a1f

Browse files
committed
Hebrew s45: phonikud knesset 1.5M weak-pretrain then gold fine-tune (s43 recipe); decontam + dedupe prep
1 parent 7fdb854 commit bd48a1f

3 files changed

Lines changed: 537 additions & 0 deletions

File tree

fetch_phonikud.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Fetch Phonikud/phonikud-data knesset_nikud_v6 (5M lines, text<TAB>phonemes)
2+
to the rababa-datasets volume.
3+
4+
Source: https://huggingface.co/datasets/Phonikud/phonikud-data
5+
Labels were machine-generated by Dicta's diacritizer (~1k high-frequency
6+
words manually corrected per their changelog). We keep it as a WEAK
7+
pretraining corpus candidate only — never as a gold fine-tune stage
8+
(no-teacher-poison rule). The .7z original is kept alongside the
9+
extracted .txt for provenance.
10+
11+
Usage:
12+
modal run --detach fetch_phonikud.py
13+
"""
14+
15+
from __future__ import annotations
16+
17+
from pathlib import Path
18+
19+
import modal
20+
21+
datasets_volume = modal.Volume.from_name("rababa-datasets", create_if_missing=True)
22+
23+
URL = "https://huggingface.co/datasets/Phonikud/phonikud-data/resolve/main/knesset_nikud_v6.txt.7z"
24+
DEST = Path("/datasets/hebrew-phonikud")
25+
26+
image = modal.Image.debian_slim(python_version="3.11").pip_install(
27+
"requests", "py7zr>=0.21"
28+
)
29+
30+
app = modal.App("rababa-fetch-phonikud", image=image)
31+
32+
33+
@app.function(timeout=45 * 60, volumes={"/datasets": datasets_volume})
34+
def fetch() -> dict:
35+
import py7zr
36+
import requests
37+
38+
DEST.mkdir(parents=True, exist_ok=True)
39+
archive = DEST / "knesset_nikud_v6.txt.7z"
40+
if not archive.exists():
41+
print(f"[fetch] {URL}", flush=True)
42+
with requests.get(URL, stream=True, timeout=120) as r:
43+
r.raise_for_status()
44+
with open(archive, "wb") as f:
45+
for chunk in r.iter_content(chunk_size=1 << 20):
46+
f.write(chunk)
47+
print(f"[fetch] {archive.stat().st_size} bytes", flush=True)
48+
49+
txt = DEST / "knesset_nikud_v6.txt"
50+
if not txt.exists():
51+
print("[extract] ...", flush=True)
52+
with py7zr.SevenZipFile(archive, mode="r") as z:
53+
z.extractall(DEST)
54+
55+
n = 0
56+
sample: list[str] = []
57+
with open(txt, encoding="utf-8") as f:
58+
for line in f:
59+
n += 1
60+
if n <= 3:
61+
sample.append(line.rstrip("\n"))
62+
print(f"[inspect] {n} lines, {txt.stat().st_size} bytes", flush=True)
63+
for s in sample:
64+
print(f"[inspect] {s[:200]}", flush=True)
65+
66+
datasets_volume.commit()
67+
return {"lines": n, "bytes": txt.stat().st_size, "sample": sample}
68+
69+
70+
@app.local_entrypoint()
71+
def main():
72+
print(fetch.remote())

inspect_phonikud.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""Inspect phonikud knesset_nikud_v6 format: column layout, marker inventory,
2+
length stats. Read-only; no writes.
3+
4+
Usage:
5+
modal run inspect_phonikud.py
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from collections import Counter
11+
from pathlib import Path
12+
13+
import modal
14+
15+
datasets_volume = modal.Volume.from_name("rababa-datasets", create_if_missing=True)
16+
SRC = Path("/datasets/hebrew-phonikud/knesset_nikud_v6.txt")
17+
18+
image = modal.Image.debian_slim(python_version="3.11")
19+
20+
app = modal.App("rababa-inspect-phonikud", image=image)
21+
22+
NIKUD = set("ׇ֑") | {chr(c) for c in range(0x05B0, 0x05C0)}
23+
24+
25+
@app.function(timeout=20 * 60, volumes={"/datasets": datasets_volume})
26+
def inspect() -> dict:
27+
datasets_volume.reload()
28+
29+
with open(SRC, encoding="utf-8") as f:
30+
head = [next(f) for _ in range(5)]
31+
for i, line in enumerate(head):
32+
cols = line.rstrip("\n").split("\t")
33+
print(f"[line {i}] n_cols={len(cols)}", flush=True)
34+
for j, c in enumerate(cols):
35+
print(f" col{j}: {c[:120]!r}", flush=True)
36+
37+
tabs = Counter()
38+
col2_markers = Counter()
39+
col2_has_nikud = col1_has_nikud = 0
40+
col1_lens = []
41+
n = 0
42+
with open(SRC, encoding="utf-8") as f:
43+
for line in f:
44+
n += 1
45+
if n > 200_000:
46+
break
47+
cols = line.rstrip("\n").split("\t")
48+
tabs[len(cols)] += 1
49+
if len(cols) != 2:
50+
continue
51+
t, p = cols
52+
col1_lens.append(len(t))
53+
if any(ch in NIKUD for ch in t):
54+
col1_has_nikud += 1
55+
if any(ch in NIKUD for ch in p):
56+
col2_has_nikud += 1
57+
for ch in p:
58+
if not ("א" <= ch <= "ת") and ch != " ":
59+
col2_markers[ch] += 1
60+
61+
col1_lens.sort()
62+
print(f"[stats] n={n} tabs_per_line={dict(tabs)}", flush=True)
63+
print(f"[stats] col1_nikud={col1_has_nikud} col2_nikud={col2_has_nikud}", flush=True)
64+
if col1_lens:
65+
print(f"[stats] col1_len p50={col1_lens[len(col1_lens)//2]} "
66+
f"p95={col1_lens[int(len(col1_lens)*0.95)]} max={col1_lens[-1]}", flush=True)
67+
print(f"[stats] col2 non-letter markers: {col2_markers.most_common(20)}", flush=True)
68+
return {"n": n, "tabs": dict(tabs)}
69+
70+
71+
@app.local_entrypoint()
72+
def main():
73+
print(inspect.remote())

0 commit comments

Comments
 (0)