Quazim0t0 commited on
Commit
cdb774c
Β·
verified Β·
1 Parent(s): d51e608

Upload web/docs/ARCHITECTURE.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. web/docs/ARCHITECTURE.md +120 -0
web/docs/ARCHITECTURE.md ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Architecture
2
+
3
+ How a page load becomes a training node, and how a handful of browsers stay
4
+ bit-identical while training one model together.
5
+
6
+ ```
7
+ browser A ──┐ β”Œβ”€β”€ browser B
8
+ WebGPU/CPU β”‚ WebRTC data channels β”‚ WebGPU/CPU
9
+ verified β”œβ”€β”€β”€β”€β”€β”€β”€β”€ gradients ────── verified
10
+ INT8 units β”‚ β”‚ INT8 units
11
+ └──── server.js (signaling + static + /data) β”€β”€β”€β”€β”˜
12
+ never sees weights or gradients
13
+ ```
14
+
15
+ ## The pieces
16
+
17
+ | File | Role |
18
+ |---|---|
19
+ | `server.js` | WebSocket **signaling** (introduces peers, relays WebRTC offers/ICE), static hosting, and the `/data` endpoint that streams FineWeb-Edu text. It never sees weights or gradients. |
20
+ | `public/app.js` | the WebRTC mesh, the training loop, gradient averaging, checkpoints, and the sync guard. |
21
+ | `public/transformer.js` | the mini transformer LM (attention + MLP blocks, next-token prediction) β€” forward and backward, every multiply through the units. |
22
+ | `public/verified_core.js` | the **verified INT8 units**: block-scaled quantize β†’ exact LUT multiply β†’ int32 accumulate β†’ bit-exact f32 epilogue; plus the JS mirrors, the audit, and the MLP chain reference. |
23
+ | `public/webgpu.js` | the WGSL kernels (LUT matmul, DP4A int8 dot, fused attention, B2B MLP chain) and the **exact init gates** that admit them. |
24
+ | `public/traincore.js` | float reference trainer + deterministic Adam. |
25
+ | `public/*.bin` | the units as lookup tables (`mul_lut` is a 65536-entry exact int8 product table). |
26
+
27
+ ## Peers, groups, and the leader
28
+
29
+ Peers are grouped by **public IP** (same network β‡’ same group) or by an
30
+ explicit `?room=CODE` (with host approval per join). Inside a group every
31
+ pair gets a WebRTC data channel β€” a full mesh; the signaling server is only
32
+ used for the handshake.
33
+
34
+ Whoever presses **Start** becomes the **leader** for that run: their settings
35
+ are broadcast to everyone, they sync mid-run joiners (weights + step), and
36
+ they are the source of truth for gradient repair (below). Everything else is
37
+ symmetric β€” every device computes, sends, and averages the same way.
38
+
39
+ ## One training step
40
+
41
+ 1. Each device samples its own batch from its data shard and computes the
42
+ loss and gradient **through the verified units**.
43
+ 2. It broadcasts `[step | weight-hash | probe-hash | loss | gradient]` to
44
+ every peer and collects everyone else's.
45
+ 3. When it has a gradient from **every device in the step roster**, it
46
+ averages them **in strict roster order** β€” float addition is not
47
+ associative, so a different order would round differently and fork the
48
+ weights β€” and applies one Adam update. Adam's state is identical
49
+ everywhere, so nothing but the gradients ever crosses the wire.
50
+ 4. Because every device starts from the same seeded weights and applies the
51
+ same averaged update with the same rounding, the replicas are
52
+ **bit-identical** β€” verified continuously by the weight hash.
53
+
54
+ ### The sync guard
55
+
56
+ Before applying an update, each device checks every peer's **weight hash**
57
+ (FNV-1a of the full weight bytes before the step) against its own. Any
58
+ mismatch means the replicas have forked β€” the guard **stops the run** rather
59
+ than train past a divergence. The **probe hash** is different: it re-runs a
60
+ fixed seeded int8 GEMM through the device's live kernel each step, so a
61
+ device whose *arithmetic* has gone wrong is caught even while its weights
62
+ still match (weights only depend on the gradient bytes everyone receives).
63
+
64
+ ### Gradient repair (asymmetric meshes)
65
+
66
+ WebRTC meshes can be asymmetric: a gradient can reach the leader but not some
67
+ follower. Instead of stalling, a follower missing a roster gradient asks the
68
+ **leader** to re-send that peer's gradient for that step (the leader retains
69
+ the last 8 steps of everyone's gradients). The repair is bit-exact β€” the
70
+ follower averages the identical bytes β€” so the run continues without a fork.
71
+ Only the leader may vouch for another peer's gradient.
72
+
73
+ ## The wire protocol
74
+
75
+ All messages are binary on the data channel. Gradient messages start with a
76
+ non-negative `int32 step`; control messages use negative sentinels:
77
+
78
+ | Sentinel | Message |
79
+ |---|---|
80
+ | `-2` | checkpoint push (one device restores the whole group) |
81
+ | `-3` | run config from the starter (`c, t, b, steps, lr`) |
82
+ | `-4` | step roster (who is in this step) |
83
+ | `-5` | fragment β€” large messages are chunked at 48 KB |
84
+ | `-6` | mid-run resume (weights + step for a late joiner) |
85
+ | `-7` | repair request (step, peer) |
86
+ | `-8` | repair response (leader-only, original header + bytes) |
87
+
88
+ Gradient wire format: `[i32 step][u32 whash][u32 phash][f32 loss][f32 grad…]`.
89
+
90
+ ## Compute backends
91
+
92
+ At init, `webgpu.js` tries the best available backend and **gates** each
93
+ kernel before use (see [VERIFICATION.md](VERIFICATION.md)):
94
+
95
+ 1. **DP4A** (`packed_4x8_integer_dot_product`) β€” hardware int8 dot product.
96
+ 2. **LUT shader** β€” the multiply table as a WebGPU storage buffer; also the
97
+ oracle twin every other kernel is compared against.
98
+ 3. **CPU (JS)** β€” the same units in plain JavaScript. Not an approximation:
99
+ the CPU mirror and the GPU kernels produce the **same bits**, which is
100
+ what lets GPU and CPU devices train in one group.
101
+
102
+ There is no plain-float forward path β€” every multiply in the model goes
103
+ through the units on every backend.
104
+
105
+ ## Training data
106
+
107
+ `server.js` exposes `/data`: it picks a random slice from the FineWeb-Edu
108
+ 10BT parquet shards and reads it **directly off the HF CDN with HTTP range
109
+ requests** (pure-JS `hyparquet`, SNAPPY decompression, results cached). No
110
+ datasets-server dependency. Browsers fetch `/data` per batch; devices that
111
+ cannot reach it fall back to a small built-in corpus. The dataset is
112
+ hardcoded β€” every group trains on FineWeb-Edu.
113
+
114
+ ## Checkpoints and the inference kit
115
+
116
+ The `.pt` checkpoint stores magic/version, config dims, step, and the flat
117
+ f32 weights; the loader validates tokenizer vocab and dimensions before
118
+ accepting, and a load **broadcasts** to the group (sentinel `-2`). The
119
+ **inference kit** is a generated single-file HTML with the model code and the
120
+ current weights (base64) baked in β€” offline generation anywhere.