DaisyChain-Train / web /test_transformer.js
Quazim0t0's picture
Web trainer upgrade: sync guard (leader roster + weight-hash divergence check), 3xINT8 fast-accurate GEMM, DP4A hardware path (verified vs units), Spikewhale tokenizer (16.5k vocab), FineWeb-Edu streaming, gradient/checkpoint fragmentation, inference kit, generation tester, contribution logs
4cf5cdc verified
Raw
History Blame Contribute Delete
2.19 kB
// Mini transformer through the verified units: (a) loss drops well below the
// uniform baseline ln(V), (b) two replicas fed the same averaged gradients stay
// bit-identical, (c) generation produces corpus-like text.
const fs = require("fs");
const path = require("path");
const T = require("./public/traincore.js");
const V = require("./public/verified_core.js");
const X = require("./public/transformer.js");
function loadLUTs() {
const p = (f) => path.join(__dirname, "public", f);
return { mul: new Int16Array(fs.readFileSync(p("mul_lut.bin")).buffer.slice(0)),
requant: new Int8Array(fs.readFileSync(p("requant_lut.bin")).buffer.slice(0)),
relu: new Int8Array(fs.readFileSync(p("relu_lut.bin")).buffer.slice(0)) };
}
const L = loadLUTs();
const matmulInt8 = (Xq, Wq, m, k, n, LL) => V.lutMatmulJS(Xq, Wq, m, k, n, LL);
const cfg = { c: 32, t: 32, b: 8, layers: 2, heads: 2, steps: 120, lr: 0.02 };
(async function () {
const A = X.init(cfg, L, matmulInt8);
const B = X.init(cfg, L, matmulInt8);
console.log(`vocab=${X.vocabSize()}, params=${A.nParams}, baseline loss=${Math.log(X.vocabSize()).toFixed(3)}`);
const oa = T.makeAdam(A.nParams, { lr: cfg.lr });
const ob = T.makeAdam(B.nParams, { lr: cfg.lr });
let first = 0, loss = 0;
for (let s = 0; s < cfg.steps; s++) {
const ra = await X.trainStep(A);
const rb = await X.trainStep(B);
const avg = T.averageGrads([ra.grad, rb.grad]);
X.applyUpdate(A, oa.step(avg));
X.applyUpdate(B, ob.step(avg));
loss = (ra.loss + rb.loss) / 2;
if (s === 0) first = loss;
if (s % 30 === 0 || s === cfg.steps - 1) console.log(` step ${s} loss ${loss.toFixed(4)}`);
}
const pa = X.getFlatParams(A), pb = X.getFlatParams(B);
let diff = 0;
for (let i = 0; i < pa.length; i++) diff = Math.max(diff, Math.abs(pa[i] - pb[i]));
const sample = await X.generate(A, "the ", 60);
console.log(`sample: "${sample}"`);
console.log(`replica max param diff: ${diff.toExponential(3)}`);
const ok = loss < first * 0.75 && loss < Math.log(X.vocabSize()) && diff === 0;
console.log(ok ? "TRANSFORMER TEST PASSED" : "TRANSFORMER TEST FAILED");
process.exit(ok ? 0 : 1);
})();