ok
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
// Vluchtige Node-test: laadt app.js met een minimale DOM-stub en
|
||||
// controleert of het neuraal netwerk echt leert (XOR / cirkel / spiraal).
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const src = fs.readFileSync(path.join(__dirname, 'app.js'), 'utf8');
|
||||
|
||||
function makeCtx() {
|
||||
const data = new Uint8ClampedArray(200 * 150 * 4);
|
||||
return {
|
||||
createImageData: () => ({ width: 200, height: 150, data }),
|
||||
putImageData: () => {}, clearRect: () => {}, drawImage: () => {},
|
||||
fillRect: () => {}, fillText: () => {}, beginPath: () => {}, arc: () => {},
|
||||
fill: () => {}, stroke: () => {}, moveTo: () => {}, lineTo: () => {}, strokeRect: () => {}
|
||||
};
|
||||
}
|
||||
function makeEl() {
|
||||
return {
|
||||
width: 460, height: 320, style: {}, textContent: '', innerHTML: '',
|
||||
scrollTop: 0, scrollHeight: 0,
|
||||
classList: { add() {}, remove() {}, toggle() { return false; } },
|
||||
appendChild() {}, addEventListener() {}, setAttribute() {}, click() {},
|
||||
getContext: makeCtx, parentElement: { clientWidth: 800 }, dataset: {}, value: '6',
|
||||
toDataURL: () => 'data:image/png;base64,xx'
|
||||
};
|
||||
}
|
||||
const documentStub = {
|
||||
getElementById: () => makeEl(),
|
||||
querySelectorAll: () => [],
|
||||
createElement: () => makeEl()
|
||||
};
|
||||
const rafNoop = () => {};
|
||||
|
||||
const code = src + '\n;globalThis.__NN = NeuralNet; globalThis.__makeDataset = makeDataset;';
|
||||
new Function('document', 'requestAnimationFrame', 'window', 'setTimeout', code)(
|
||||
documentStub, rafNoop, { addEventListener() {} }, setTimeout
|
||||
);
|
||||
|
||||
const NeuralNet = globalThis.__NN;
|
||||
const makeDataset = globalThis.__makeDataset;
|
||||
|
||||
function trainOn(name, epochs, lr, hidden) {
|
||||
const net = new NeuralNet([2, hidden, 1]);
|
||||
const samples = makeDataset(name);
|
||||
const idx = samples.map((_, i) => i);
|
||||
let last = null;
|
||||
for (let e = 0; e < epochs; e++) {
|
||||
for (let i = idx.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const t = idx[i]; idx[i] = idx[j]; idx[j] = t;
|
||||
}
|
||||
let sum = 0;
|
||||
for (const i of idx) sum += net.trainSample(samples[i].x, samples[i].y, lr);
|
||||
last = sum / idx.length;
|
||||
}
|
||||
const test = makeDataset(name);
|
||||
const n = Math.min(200, test.length);
|
||||
let correct = 0;
|
||||
for (let i = 0; i < n; i++) {
|
||||
if ((net.predict(test[i].x) > 0.5 ? 1 : 0) === test[i].y) correct++;
|
||||
}
|
||||
return { acc: (correct / n) * 100, loss: last };
|
||||
}
|
||||
|
||||
// Finale controle — zelfde instelling als app.js (Adam, lr 0.05)
|
||||
function trial(name, hidden, epochs) {
|
||||
const res = [];
|
||||
for (let t = 0; t < 6; t++) res.push(trainOn(name, epochs, 0.05, hidden));
|
||||
const worst = Math.min(...res.map((a) => a.acc));
|
||||
return { worst, raw: res.map((a) => a.acc.toFixed(1) + '%').join(' ') };
|
||||
}
|
||||
|
||||
const xor0 = trainOn('XOR', 0, 0.05, 10);
|
||||
const xor = trial('XOR', 10, 250);
|
||||
const cir = trial('CIRKEL', 14, 400);
|
||||
const spi = trial('SPIRAAL', 30, 1400);
|
||||
|
||||
console.log('XOR 0ep :', xor0.acc.toFixed(1) + '%');
|
||||
console.log('XOR 250ep :', xor.raw, ' (slechtste ' + xor.worst.toFixed(1) + '%)');
|
||||
console.log('CIRKEL 400ep :', cir.raw, ' (slechtste ' + cir.worst.toFixed(1) + '%)');
|
||||
console.log('SPIRAAL 1400ep :', spi.raw, ' (slechtste ' + spi.worst.toFixed(1) + '%)');
|
||||
|
||||
let fail = 0;
|
||||
if (xor0.acc < 35 || xor0.acc > 70) { console.error('FAIL: XOR ongetraind moet ~50% zijn'); fail = 1; }
|
||||
if (xor.worst < 85) { console.error('FAIL: XOR convergerde niet'); fail = 1; }
|
||||
if (cir.worst < 90) { console.error('FAIL: Cirkel convergerde niet'); fail = 1; }
|
||||
if (spi.worst < 85) { console.error('FAIL: Spiraal convergerde niet'); fail = 1; }
|
||||
|
||||
if (fail) {
|
||||
console.error('VALIDATIE MISLUKT');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('PASS: alle drie datasets leren betrouwbaar met Adam (lr 0.05).');
|
||||
process.exit(0);
|
||||
|
||||
if (fail) process.exit(1);
|
||||
console.log('PASS: app.js laadt schoon én het netwerk leert alle drie datasets.');
|
||||
process.exit(0);
|
||||
Reference in New Issue
Block a user