File size: 2,064 Bytes
4fd620e | 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 | // Verifies the training loop + 2-peer gradient averaging in pure Node (no
// browser). Simulates two peers each holding half the data; they average
// gradients every step. Proves: (a) it converges, (b) both replicas stay
// identical — the same guarantees the browser P2P version needs.
const T = require("./public/traincore.js");
function randn(n) {
const a = new Float32Array(n);
for (let i = 0; i < n; i++) {
let u = 0, v = 0;
while (u === 0) u = Math.random();
while (v === 0) v = Math.random();
a[i] = Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);
}
return a;
}
const din = 16, dout = 4, nPer = 128, steps = 400, lr = 0.05;
// ground-truth weights
const Wtrue = randn(din * dout);
function makeShard() {
const X = randn(nPer * din);
const y = T.matmul(X, Wtrue, nPer, din, dout); // clean targets
return { X, y };
}
const A = makeShard(), B = makeShard();
// both peers start from the SAME W0 (initiator broadcasts it)
const W0 = randn(din * dout);
const Wa = Float32Array.from(W0), Wb = Float32Array.from(W0);
let loss = 0;
for (let s = 0; s < steps; s++) {
const ra = T.forwardLossGrad(A.X, A.y, Wa, nPer, din, dout);
const rb = T.forwardLossGrad(B.X, B.y, Wb, nPer, din, dout);
const avg = T.averageGrads([ra.gradW, rb.gradW]); // <-- exchanged P2P
T.applyGrad(Wa, avg, lr);
T.applyGrad(Wb, avg, lr);
loss = (ra.loss + rb.loss) / 2;
if (s % 80 === 0 || s === steps - 1)
console.log(` step ${s} cluster-avg loss ${loss.toFixed(6)}`);
}
let maxDiff = 0;
for (let i = 0; i < Wa.length; i++) maxDiff = Math.max(maxDiff, Math.abs(Wa[i] - Wb[i]));
let recovery = 0;
for (let i = 0; i < Wtrue.length; i++) recovery = Math.max(recovery, Math.abs(Wa[i] - Wtrue[i]));
console.log(`\nreplica max param diff: ${maxDiff.toExponential(3)}`);
console.log(`max |W - W_true|: ${recovery.toExponential(3)}`);
const ok = loss < 1e-3 && maxDiff < 1e-9 && recovery < 0.05;
console.log(ok ? "\nCORE TEST PASSED — converged, replicas in sync." : "\nCORE TEST FAILED");
process.exit(ok ? 0 : 1);
|