Skip to content

Commit 0c3c8e4

Browse files
committed
Getting train and test acc!
1 parent 64efb6a commit 0c3c8e4

1 file changed

Lines changed: 30 additions & 11 deletions

File tree

benchmarks/nn.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ def fashion_mnist():
5151
images = ds["images"].astype("float64").values
5252
labels = ds["labels"].values.astype("int64")
5353
except Exception:
54-
# Offline fallback: a separable synthetic set (per-class template + noise),
55-
# so the same pipeline still learns without the network.
54+
# Offline fallback: a separable synthetic set (per-class template +
55+
# noise), so the same pipeline still learns without the network.
5656
rng = np.random.default_rng(0)
5757
n = N_TRAIN + N_TEST
5858
templates = rng.standard_normal((10, SIDE, SIDE))
@@ -115,7 +115,7 @@ def main():
115115
},
116116
)
117117

118-
frac = N_TRAIN / (N_TRAIN + N_TEST) # ratio: ~0.7
118+
frac = N_TRAIN / (N_TRAIN + N_TEST) # default ratio: ~0.7
119119
# Train-test split
120120
data = ctx.sql(f"""
121121
SELECT sample,
@@ -130,7 +130,7 @@ def main():
130130
).to_pandas()["n"][0]
131131

132132
def init_weight(inp: int, out: int):
133-
"""Small random weights over ``inp`` inputs, with a zero bias row appended."""
133+
"""Small random weights with a zero bias row appended."""
134134
weight = rng.standard_normal((inp, out)) * 0.1
135135
bias = np.zeros((1, out))
136136
return np.concatenate((weight, bias), axis=0) # (inp + 1, out)
@@ -161,20 +161,23 @@ def init_weight(inp: int, out: int):
161161
# Each layer augments its activation with a constant-1 bias unit (
162162
# index = width), contracts with the weight table (JOIN on the shared
163163
# index + grouped SUM), and keeps the pre-activation z (tanh(z) for
164-
# hidden, softmax later). .cache() materialises each stage so the
164+
# hidden, linear output). .cache() materialises each stage so the
165165
# per-step plan stays flat.
166+
#
167+
# The forward runs over ALL samples: train rows drive learning, test
168+
# rows ride along so we can score them from the same logits. Only delta2
169+
# is restricted to train, so the gradients (and the trained weights) are
170+
# identical to a train-only forward — test is never backpropagated.
166171
fwd0 = ctx.sql(f"""
167172
WITH a AS (
168173
SELECT sample, height * {SIDE} + width AS inp, images AS val
169174
FROM mnist.pixels
170-
WHERE sample IN (SELECT sample FROM data WHERE split = 'train')
171175
UNION ALL
172176
-- the constant-1 bias unit
173177
SELECT sample,
174178
(SELECT DISTINCT width FROM weight WHERE layer = 0) AS inp,
175179
1.0 AS val
176180
FROM mnist.labels
177-
WHERE sample IN (SELECT sample FROM data WHERE split = 'train')
178181
)
179182
SELECT a.sample, w.out AS out, SUM(a.val * w.val) AS z,
180183
tanh(SUM(a.val * w.val)) AS val
@@ -230,6 +233,8 @@ def init_weight(inp: int, out: int):
230233
e.e / s.s - CASE WHEN e.out = y.labels THEN 1.0 ELSE 0.0 END AS val
231234
FROM e JOIN s ON e.sample = s.sample
232235
JOIN mnist.labels y ON y.sample = e.sample
236+
-- restrict the error to train, so every downstream gradient is train-only
237+
WHERE e.sample IN (SELECT sample FROM data WHERE split = 'train')
233238
""").cache()
234239
ctx.deregister_table("delta2")
235240
ctx.register_table("delta2", delta2)
@@ -337,6 +342,7 @@ def init_weight(inp: int, out: int):
337342
ctx.register_table("weight", w)
338343

339344
if step % 5 == 0 or step == STEPS - 1:
345+
# Train cross-entropy (logits span all samples, so filter to train).
340346
loss = ctx.sql(f"""
341347
WITH m AS (SELECT sample, MAX(z) AS m FROM logits GROUP BY sample),
342348
e AS (SELECT logits.sample, logits.out, exp(logits.z - m.m) AS e
@@ -346,17 +352,30 @@ def init_weight(inp: int, out: int):
346352
FROM e JOIN s ON e.sample = s.sample
347353
JOIN mnist.labels y ON y.sample = e.sample
348354
WHERE e.out = y.labels
355+
AND e.sample IN (SELECT sample FROM data WHERE split = 'train')
349356
""").to_pandas()["loss"][0]
350-
acc = ctx.sql(f"""
357+
# Accuracy per split: argmax the shared logits, join the split label.
358+
# Both come from the one all-samples forward — no second pass.
359+
acc = (
360+
ctx.sql(f"""
351361
WITH pred AS (
352362
SELECT sample, out,
353363
ROW_NUMBER() OVER (PARTITION BY sample ORDER BY z DESC) AS rk
354364
FROM logits)
355-
SELECT AVG(CASE WHEN p.out = y.labels THEN 1.0 ELSE 0.0 END) AS acc
365+
SELECT d.split,
366+
AVG(CASE WHEN p.out = y.labels THEN 1.0 ELSE 0.0 END) AS acc
356367
FROM pred p JOIN mnist.labels y ON p.sample = y.sample
368+
JOIN data d ON d.sample = p.sample
357369
WHERE p.rk = 1
358-
""").to_pandas()["acc"][0]
359-
print(f"step {step:2d}: loss {loss:.3f} train_acc {acc:.3f}")
370+
GROUP BY d.split
371+
""")
372+
.to_pandas()
373+
.set_index("split")["acc"]
374+
)
375+
print(
376+
f"step {step:2d}: loss {loss:.3f} "
377+
f"train_acc {acc['train']:.3f} test_acc {acc['test']:.3f}"
378+
)
360379

361380
# The trained weights come back out as xarray as one relation: a ragged
362381
# weight(layer, inp, out) array (absent cells are NaN where layers are narrower).

0 commit comments

Comments
 (0)