File size: 3,961 Bytes
d8edfe6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
"""v3.1b decoder package: the shipped t16/t32/t64/t96 (B=3 scorers) + the next-token n16/n32/n64/n96 (B=1,
reports/ane-dec.md "next-token function") in ONE multifunction package with shared weights, from the same parts.
  1. the parts are the shipped ones: a t-only merge of ane/dec/mf/parts/*_fp16 has the shipped
     ane/dec/mf/dec_mf_fp16.mlpackage's weight.bin byte for byte and the same t16..t96 program functions;
  2. t + n merge -> McBopomofoLM/build/v31b/dec_mf_tn_fp16.mlpackage (default function t16, as shipped): its t16..t96
     program functions equal the shipped ones and its weight.bin holds the shipped blobs at the same offsets (only
     the header's blob count differs), so every weight a t function reads is the shipped bytes;
  3. SlothE/v31b/dec_next_first.tsv: char \t token ids whose decoded text starts with that char (partial-byte and
     whitespace-initial tokens dropped) = typo/t2.py _dinit / typo/t2c.py, the study's first-char grouping.
usage: nice -n 15 $W/ane/dec/.venv/bin/python SlothE/v31b/build_dec_tn.py
"""
import hashlib
import shutil
import sys
import tempfile
from pathlib import Path

HERE = Path(__file__).resolve().parent
W = HERE.parents[2]
DEC = W / "ane" / "dec"
sys.path.insert(0, str(DEC))
import coremltools as ct  # noqa: E402
from coremltools.models.utils import MultiFunctionDescriptor, save_multifunction  # noqa: E402
from tokenizers import decoders  # noqa: E402
from common import TOK  # noqa: E402

TS = (16, 32, 64, 96)
OUT = W / "McBopomofoLM" / "build" / "v31b"


def sha(p):
    return hashlib.sha256(Path(p).read_bytes()).hexdigest()


def merge(fns, dst):
    d = MultiFunctionDescriptor()
    for name, p in fns.items():
        d.add_function(str(p), src_function_name="main", target_function_name=name)
    d.default_function_name = "t16"
    shutil.rmtree(dst, ignore_errors=True)
    save_multifunction(d, str(dst))


tparts = {f"t{T}": DEC / "mf" / "parts" / f"dec_b3_t{T}_fp16.mlpackage" for T in TS}
nparts = {f"n{T}": DEC / "mf_next" / "parts" / f"dec_b1_n{T}_fp16.mlpackage" for T in TS}
SHIPPED = DEC / "mf" / "dec_mf_fp16.mlpackage"
shipped_spec = ct.utils.load_spec(str(SHIPPED))
shipped_w = (SHIPPED / "Data" / "com.apple.CoreML" / "weights" / "weight.bin").read_bytes()


def same_t(pkg):
    spec = ct.utils.load_spec(str(pkg))
    w = (Path(pkg) / "Data" / "com.apple.CoreML" / "weights" / "weight.bin").read_bytes()
    fns = all(spec.mlProgram.functions[f"t{T}"] == shipped_spec.mlProgram.functions[f"t{T}"] for T in TS)
    # the 64-byte storage header starts with the blob count; every blob after it must be the shipped bytes
    return fns, w[64:len(shipped_w)] == shipped_w[64:], len(w)


with tempfile.TemporaryDirectory() as tmp:
    t_only = Path(tmp) / "t_only.mlpackage"
    merge(tparts, t_only)
    fns, prefix, n = same_t(t_only)
    print(f"t-only merge vs shipped: t functions equal {fns}, weight blobs equal {prefix and n == len(shipped_w)}")
    assert fns and prefix and n == len(shipped_w), "the t parts are not the shipped ones"
OUT.mkdir(parents=True, exist_ok=True)
dst = OUT / "dec_mf_tn_fp16.mlpackage"
merge(tparts | nparts, dst)
fns, prefix, n = same_t(dst)
size = sum(f.stat().st_size for f in dst.rglob("*") if f.is_file()) / 1e6
print(f"merged t16..t96 + n16..n96 -> {dst} {size:.1f} MB; t functions equal shipped {fns}; "
      f"weight.bin = the shipped blobs at the same offsets {prefix} (+{n - len(shipped_w)} B of n-only blobs appended)")
assert fns and prefix
if TOK.decoder is None:
    TOK.decoder = decoders.ByteLevel()
first = {}
for t in range(TOK.get_vocab_size()):
    s = TOK.decode([t])
    if s and s[0] != "�" and not s[0].isspace():
        first.setdefault(s[0], []).append(t)
with open(HERE / "dec_next_first.tsv", "w", encoding="utf-8") as f:
    for c in sorted(first):
        f.write(c + "\t" + " ".join(map(str, first[c])) + "\n")
print(f"first-char table: {len(first)} chars, {sum(map(len, first.values()))} tokens")