Skip to content

Commit 64efb6a

Browse files
committed
test train split in SQL!
1 parent f4cc104 commit 64efb6a

1 file changed

Lines changed: 50 additions & 27 deletions

File tree

benchmarks/nn.py

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ def main():
115115
},
116116
)
117117

118+
frac = N_TRAIN / (N_TRAIN + N_TEST) # ratio: ~0.7
119+
# Train-test split
120+
data = ctx.sql(f"""
121+
SELECT sample,
122+
CASE WHEN random() < {frac} THEN 'train' ELSE 'test' END AS split
123+
FROM mnist.labels
124+
""").cache()
125+
ctx.register_table("data", data)
126+
# The gradient averages over the actual train count (random, ~frac * N),
127+
# read once from the materialized split.
128+
n_train = ctx.sql(
129+
"SELECT COUNT(*) AS n FROM data WHERE split = 'train'"
130+
).to_pandas()["n"][0]
131+
118132
def init_weight(inp: int, out: int):
119133
"""Small random weights over ``inp`` inputs, with a zero bias row appended."""
120134
weight = rng.standard_normal((inp, out)) * 0.1
@@ -142,23 +156,25 @@ def init_weight(inp: int, out: int):
142156

143157
for step in range(STEPS):
144158
#
145-
# --- forward pass ---------------------------------------------------------
146-
#
147-
# Each layer augments its activation with a constant-1 bias unit (index =
148-
# width), contracts with the weight table (JOIN on the shared index + grouped
149-
# SUM), and keeps the pre-activation z (tanh(z) for hidden, softmax later).
150-
# .cache() materialises each stage so the per-step plan stays flat.
159+
# --- forward pass -----------------------------------------------------
151160
#
161+
# Each layer augments its activation with a constant-1 bias unit (
162+
# index = width), contracts with the weight table (JOIN on the shared
163+
# index + grouped SUM), and keeps the pre-activation z (tanh(z) for
164+
# hidden, softmax later). .cache() materialises each stage so the
165+
# per-step plan stays flat.
152166
fwd0 = ctx.sql(f"""
153167
WITH a AS (
154168
SELECT sample, height * {SIDE} + width AS inp, images AS val
155-
FROM mnist.pixels WHERE sample < {N_TRAIN}
169+
FROM mnist.pixels
170+
WHERE sample IN (SELECT sample FROM data WHERE split = 'train')
156171
UNION ALL
157172
-- the constant-1 bias unit
158173
SELECT sample,
159174
(SELECT DISTINCT width FROM weight WHERE layer = 0) AS inp,
160175
1.0 AS val
161-
FROM mnist.labels WHERE sample < {N_TRAIN}
176+
FROM mnist.labels
177+
WHERE sample IN (SELECT sample FROM data WHERE split = 'train')
162178
)
163179
SELECT a.sample, w.out AS out, SUM(a.val * w.val) AS z,
164180
tanh(SUM(a.val * w.val)) AS val
@@ -200,10 +216,11 @@ def init_weight(inp: int, out: int):
200216
ctx.deregister_table("logits")
201217
ctx.register_table("logits", logits)
202218
#
203-
# --- backward pass --------------------------------------------------------
219+
# --- backward pass ----------------------------------------------------
204220
#
205-
# Output error delta2 = softmax(logits) - onehot(label). The one hand-derived
206-
# rule: softmax couples classes through a per-sample normaliser.
221+
# Output error delta2 = softmax(logits) - onehot(label). The one
222+
# hand-derived rule: softmax couples classes through a per-sample
223+
# normaliser.
207224
delta2 = ctx.sql(f"""
208225
WITH m AS (SELECT sample, MAX(z) AS m FROM logits GROUP BY sample),
209226
e AS (SELECT logits.sample, logits.out, exp(logits.z - m.m) AS e
@@ -212,13 +229,14 @@ def init_weight(inp: int, out: int):
212229
SELECT e.sample, e.out,
213230
e.e / s.s - CASE WHEN e.out = y.labels THEN 1.0 ELSE 0.0 END AS val
214231
FROM e JOIN s ON e.sample = s.sample
215-
JOIN mnist.labels y ON y.sample = e.sample AND y.sample < {N_TRAIN}
232+
JOIN mnist.labels y ON y.sample = e.sample
216233
""").cache()
217234
ctx.deregister_table("delta2")
218235
ctx.register_table("delta2", delta2)
219236

220-
# Weight gradient of layer 2: (bias-augmented fwd1).T @ delta2 / N. The bias
221-
# row (inp = width) falls out for free — its gradient is the mean error.
237+
# Weight gradient of layer 2: (bias-augmented fwd1).T @ delta2 / N.
238+
# The bias row (inp = width) falls out for free — its gradient is the
239+
# mean error.
222240
g2 = ctx.sql(f"""
223241
WITH a AS (
224242
SELECT sample, out AS inp, val FROM fwd1
@@ -227,15 +245,16 @@ def init_weight(inp: int, out: int):
227245
(SELECT DISTINCT width FROM weight WHERE layer = 2) AS inp,
228246
1.0 AS val FROM fwd1
229247
)
230-
SELECT a.inp AS inp, d.out AS out, SUM(a.val * d.val) / {N_TRAIN} AS val
248+
SELECT a.inp AS inp, d.out AS out, SUM(a.val * d.val) / {n_train} AS val
231249
FROM a JOIN delta2 d ON a.sample = d.sample
232250
GROUP BY a.inp, d.out
233251
""").cache()
234252
ctx.deregister_table("g2")
235253
ctx.register_table("g2", g2)
236254

237-
# Propagate to layer 1: delta1 = (delta2 @ W2[non-bias].T) * tanh'(z1). The
238-
# local derivative is grad(tanh(z), z) at fwd1's pre-activation.
255+
# Propagate to layer 1: delta1 = (delta2 @ W2[non-bias].T) * tanh'(
256+
# z1). The local derivative is grad(tanh(z), z) at fwd1's
257+
# pre-activation.
239258
delta1 = ctx.sql(f"""
240259
WITH dc AS (
241260
SELECT d.sample, w.inp AS out, SUM(d.val * w.val) AS val
@@ -258,7 +277,7 @@ def init_weight(inp: int, out: int):
258277
(SELECT DISTINCT width FROM weight WHERE layer = 1) AS inp,
259278
1.0 AS val FROM fwd0
260279
)
261-
SELECT a.inp AS inp, d.out AS out, SUM(a.val * d.val) / {N_TRAIN} AS val
280+
SELECT a.inp AS inp, d.out AS out, SUM(a.val * d.val) / {n_train} AS val
262281
FROM a JOIN delta1 d ON a.sample = d.sample
263282
GROUP BY a.inp, d.out
264283
""").cache()
@@ -283,24 +302,27 @@ def init_weight(inp: int, out: int):
283302
g0 = ctx.sql(f"""
284303
WITH a AS (
285304
SELECT sample, height * {SIDE} + width AS inp, images AS val
286-
FROM mnist.pixels WHERE sample < {N_TRAIN}
305+
FROM mnist.pixels
306+
WHERE sample IN (SELECT sample FROM data WHERE split = 'train')
287307
UNION ALL
288-
SELECT sample, (SELECT DISTINCT width FROM weight WHERE layer = 0) AS inp,
308+
SELECT sample,
309+
(SELECT DISTINCT width FROM weight WHERE layer = 0) AS inp,
289310
1.0 AS val
290-
FROM mnist.labels WHERE sample < {N_TRAIN}
311+
FROM mnist.labels
312+
WHERE sample IN (SELECT sample FROM data WHERE split = 'train')
291313
)
292-
SELECT a.inp AS inp, d.out AS out, SUM(a.val * d.val) / {N_TRAIN} AS val
314+
SELECT a.inp AS inp, d.out AS out, SUM(a.val * d.val) / {n_train} AS val
293315
FROM a JOIN delta0 d ON a.sample = d.sample
294316
GROUP BY a.inp, d.out
295317
""").cache()
296318
ctx.deregister_table("g0")
297319
ctx.register_table("g0", g0)
298320

299321
#
300-
# --- SGD update: one query over the whole relation -----------------------
322+
# --- SGD update: one query over the whole relation --------------------
301323
#
302-
# weight <- weight - lr * gradient, joining every layer at once against the
303-
# per-layer gradients tagged with their layer index.
324+
# weight <- weight - lr * gradient, joining every layer at once
325+
# against the per-layer gradients tagged with their layer index.
304326
w = ctx.sql(f"""
305327
WITH grad AS (
306328
SELECT 0 AS layer, inp, out, val FROM g0
@@ -322,7 +344,7 @@ def init_weight(inp: int, out: int):
322344
s AS (SELECT sample, SUM(e) AS s FROM e GROUP BY sample)
323345
SELECT -AVG(ln(e.e / s.s)) AS loss
324346
FROM e JOIN s ON e.sample = s.sample
325-
JOIN mnist.labels y ON y.sample = e.sample AND y.sample < {N_TRAIN}
347+
JOIN mnist.labels y ON y.sample = e.sample
326348
WHERE e.out = y.labels
327349
""").to_pandas()["loss"][0]
328350
acc = ctx.sql(f"""
@@ -346,7 +368,8 @@ def init_weight(inp: int, out: int):
346368
print(f"trained {WIDTHS} MLP; weights -> xarray {dict(trained.sizes)}.")
347369
print(trained)
348370
trained.to_zarr(
349-
f"fashion_mnist_mlp_{datetime.datetime.now().isoformat(timespec='minutes')}.zarr"
371+
f"fashion_mnist_mlp_"
372+
f"{datetime.datetime.now().isoformat(timespec='seconds')}.zarr"
350373
)
351374

352375

0 commit comments

Comments
 (0)