File size: 3,384 Bytes
b2ad782
aba2c0a
b2ad782
 
 
aba2c0a
b2ad782
aba2c0a
 
 
b2ad782
aba2c0a
 
b2ad782
 
 
 
aba2c0a
 
b2ad782
aba2c0a
 
 
 
 
 
 
 
 
 
 
b2ad782
 
 
 
aba2c0a
b2ad782
 
 
aba2c0a
 
b2ad782
aba2c0a
 
 
b2ad782
aba2c0a
b2ad782
 
 
 
 
 
aba2c0a
 
 
b2ad782
 
 
aba2c0a
 
 
 
b2ad782
aba2c0a
 
 
 
 
 
 
 
b2ad782
 
aba2c0a
 
 
 
b2ad782
 
 
 
 
 
aba2c0a
 
 
b2ad782
 
aba2c0a
b2ad782
 
 
aba2c0a
b2ad782
 
 
aba2c0a
b2ad782
aba2c0a
b2ad782
aba2c0a
 
 
 
b2ad782
aba2c0a
 
 
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""train point-1 on tinyshakespeare.

fits logits[b] = w * h[b] to the byte histogram. the loss is convex in one
variable so newton's method gets the exact optimum. writes model.safetensors,
config.json, report.txt.
"""
import json, math, os, struct, urllib.request

CORPUS_URL = "https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt"
CORPUS_LOCAL = "corpus.txt"
BYTES_VOCAB = [10] + list(range(32, 127))
H = {b: max(-1.0, min(1.0, (b - 79) / 47.0)) for b in BYTES_VOCAB}


def get_corpus():
    if not os.path.exists(CORPUS_LOCAL):
        urllib.request.urlretrieve(CORPUS_URL, CORPUS_LOCAL)
    return CORPUS_LOCAL


def histogram(path):
    counts = dict.fromkeys(BYTES_VOCAB, 0)
    total = 0
    with open(path, "rb") as f:
        while chunk := f.read(65536):
            for b in chunk:
                if b in counts:
                    counts[b] += 1
            total += len(chunk)
    return counts, total


def write_safetensors(w):
    payload = struct.pack("<f", w)
    header = {"w": {"dtype": "F32", "shape": [1], "data_offsets": [0, 4]}}
    hb = json.dumps(header, separators=(",", ":")).encode()
    with open("model.safetensors", "wb") as f:
        f.write(struct.pack("<Q", len(hb)) + hb + payload)


def main():
    counts, total = histogram(get_corpus())
    n = float(total)

    uniform_loss = math.log(len(BYTES_VOCAB))
    floor = -sum((c / n) * math.log(c / n) for c in counts.values() if c)

    ch = sum(counts[b] * H[b] for b in BYTES_VOCAB) / n

    # newton on L(w) = -(sum c_b (w h_b - logZ)) / n
    w = 0.0
    log = []
    for step in range(60):
        ex = [math.exp(w * H[b]) for b in BYTES_VOCAB]
        z = sum(ex)
        eh = sum(e * H[b] for e, b in zip(ex, BYTES_VOCAB)) / z
        var = sum(e * H[b] ** 2 for e, b in zip(ex, BYTES_VOCAB)) / z - eh * eh
        loss = -(sum(counts[b] * (w * H[b] - math.log(z)) for b in BYTES_VOCAB)) / n
        log.append((step, w, loss))
        if abs(eh - ch) < 1e-12:
            break
        w -= (eh - ch) / max(var, 1e-12)

    write_safetensors(w)

    cfg = {
        "architectures": ["Point1ForCausalLM"],
        "model_type": "point1",
        "vocab_size": len(BYTES_VOCAB),
        "byte_feature": "(b - 79) / 47 clipped to [-1, 1]",
        "auto_map": {
            "AutoConfig": "modeling_point1.Point1Config",
            "AutoModelForCausalLM": "modeling_point1.Point1ForCausalLM",
        },
    }
    with open("config.json", "w") as f:
        json.dump(cfg, f, indent=2)

    psum = sum(math.exp(w * H[b]) for b in BYTES_VOCAB)
    ranked = sorted(
        ((math.exp(w * H[b]) / psum, chr(b) if b != 10 else "\\n") for b in BYTES_VOCAB),
        reverse=True,
    )

    lines = [
        "POINT-1 TRAINING REPORT",
        "=" * 52,
        f"corpus       tinyshakespeare, {total:,} bytes",
        "parameters   1",
        "",
    ]
    lines += [f"newton {s:>3}  w = {wi:+.6f}  loss = {li:.4f}" for s, wi, li in log]
    lines += [
        "",
        f"uniform      {uniform_loss:.4f} nats/byte",
        f"unigram floor {floor:.4f}",
        f"point-1      {loss:.4f}",
        "",
        "top tokens:",
    ]
    lines += [f"  '{t}'  {p:.4f}" for p, t in ranked[:8]]
    report = "\n".join(lines)
    print(report)
    with open("report.txt", "w") as f:
        f.write(report + "\n")


if __name__ == "__main__":
    main()