appvoid commited on
Commit
9bfdf90
·
verified ·
1 Parent(s): 7666f30

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +144 -477
index.html CHANGED
@@ -318,486 +318,170 @@
318
  </div>
319
 
320
  <script>
321
- class ReinforcementModule {
322
- constructor(network, options = {}) {
323
- this.network = network;
324
- this.options = {
325
- memorySize: options.memorySize || 128,
326
- batchSize: options.batchSize || 16,
327
- learningRate: options.learningRate || 0.01,
328
- gamma: options.gamma || 0.9,
329
- epsilon: options.epsilon || 1,
330
- epsilonMin: options.epsilonMin || 0.01,
331
- epsilonDecay: options.epsilonDecay || 0.95,
332
- weightUpdateRange: options.weightUpdateRange || 0.02,
333
- actionSpace: options.actionSpace || 2048,
334
- memoryLayerSize: options.memoryLayerSize || 32,
335
- predictionHorizon: options.predictionHorizon || 16,
336
- memoryCellDecay: options.memoryCellDecay || 0.9
337
- };
338
-
339
- // Initialize memory cells
340
- this.memoryCells = {
341
- shortTerm: new Array(this.options.memoryLayerSize).fill(0),
342
- longTerm: new Array(this.options.memoryLayerSize).fill(0),
343
- cellState: new Array(this.options.memoryLayerSize).fill(0)
344
- };
345
-
346
- // Initialize gates and networks
347
- this.gates = {
348
- forget: this.createGateNetwork(this.options.memoryLayerSize),
349
- input: this.createGateNetwork(this.options.memoryLayerSize),
350
- output: this.createGateNetwork(this.options.memoryLayerSize),
351
- candidates: this.createGateNetwork(this.options.memoryLayerSize)
352
- };
353
-
354
- this.memory = [];
355
- this.currentState = this.getNetworkState();
356
- this.bestWeights = this.cloneWeights(network.weights);
357
- this.bestLoss = Infinity;
358
- this.epsilon = this.options.epsilon;
359
-
360
- this.qNetwork = this.createQNetwork();
361
- this.outcomePredictor = this.createOutcomePredictor();
362
- }
363
-
364
- createGateNetwork(size) {
365
- const gate = new carbono(false);
366
- gate.layer(this.getFlattenedStateSize(), size, "sigmoid");
367
- return gate;
368
- }
369
-
370
- createQNetwork() {
371
- const qNet = new carbono(false);
372
- const stateSize = this.getFlattenedStateSize();
373
- const actionSize = this.getActionSpaceSize();
374
-
375
- qNet.layer(stateSize + actionSize, 16, "selu");
376
- qNet.layer(16, 16, "selu");
377
- qNet.layer(16, 1, "selu");
378
-
379
- return qNet;
380
- }
381
-
382
- createOutcomePredictor() {
383
- const predictor = new carbono(false);
384
- const inputSize =
385
- this.getFlattenedStateSize() + this.options.memoryLayerSize * 3;
386
-
387
- predictor.layer(inputSize, 8, "tanh");
388
- predictor.layer(8, 8, "tanh");
389
- predictor.layer(8, this.options.predictionHorizon, "tanh");
390
-
391
- return predictor;
392
- }
393
-
394
- getFlattenedStateSize() {
395
- let size = 0;
396
- this.network.weights.forEach((layer) => {
397
- size += layer.flat().length;
398
- });
399
- return size + 3;
400
- }
401
-
402
- getActionSpaceSize() {
403
- let size = 0;
404
- this.network.weights.forEach((layer) => {
405
- size += layer.flat().length * this.options.actionSpace;
406
- });
407
- return size;
408
- }
409
-
410
- getNetworkState() {
411
- const flatWeights = this.network.weights
412
- .map((layer) => layer.flat())
413
- .flat();
414
- return [...flatWeights, this.bestLoss, this.getCurrentLoss(), this.epsilon];
415
- }
416
-
417
- async getCurrentLoss() {
418
- let totalLoss = 0;
419
- for (const data of this.network.trainingData) {
420
- const prediction = this.network.predict(data.input);
421
- totalLoss += Math.abs(prediction[0] - data.output[0]);
422
- }
423
- return totalLoss / this.network.trainingData.length;
424
- }
425
-
426
- async updateMemoryCells(state) {
427
- const forgetGate = this.gates.forget.predict(state);
428
- const inputGate = this.gates.input.predict(state);
429
- const outputGate = this.gates.output.predict(state);
430
- const candidates = this.gates.candidates.predict(state);
431
-
432
- for (let i = 0; i < this.options.memoryLayerSize; i++) {
433
- this.memoryCells.cellState[i] *= forgetGate[i];
434
- this.memoryCells.cellState[i] += inputGate[i] * candidates[i];
435
- this.memoryCells.shortTerm[i] =
436
- Math.tanh(this.memoryCells.cellState[i]) * outputGate[i];
437
- this.memoryCells.longTerm[i] =
438
- this.memoryCells.longTerm[i] * this.options.memoryCellDecay +
439
- this.memoryCells.shortTerm[i] * (1 - this.options.memoryCellDecay);
440
- }
441
- }
442
-
443
- async predictOutcomes(state) {
444
- const input = [
445
- ...state,
446
- ...this.memoryCells.shortTerm,
447
- ...this.memoryCells.longTerm,
448
- ...this.memoryCells.cellState
449
- ];
450
- return this.outcomePredictor.predict(input);
451
- }
452
-
453
- encodeAction(action) {
454
- const encoded = new Array(this.getActionSpaceSize()).fill(0);
455
- encoded[action] = 1;
456
- return encoded;
457
- }
458
-
459
- async predictQValue(state, action) {
460
- const encoded = this.encodeAction(action);
461
- const input = [...state, ...encoded];
462
- const qValue = this.qNetwork.predict(input);
463
- return qValue[0];
464
- }
465
-
466
- simulateAction(state, action) {
467
- const simState = [...state];
468
- const updates = this.actionToWeightUpdates(action);
469
- let stateIndex = 0;
470
-
471
- for (const layer of updates) {
472
- for (const row of layer) {
473
- for (const update of row) {
474
- simState[stateIndex] += update;
475
- stateIndex++;
476
- }
477
- }
478
- }
479
-
480
- return simState;
481
- }
482
-
483
- async selectAction() {
484
- if (Math.random() < this.epsilon) {
485
- return Math.floor(Math.random() * this.getActionSpaceSize());
486
- }
487
-
488
- const state = this.getNetworkState();
489
- await this.updateMemoryCells(state);
490
-
491
- let bestAction = 0;
492
- let bestOutcome = -Infinity;
493
-
494
- for (let action = 0; action < this.getActionSpaceSize(); action++) {
495
- const simState = this.simulateAction(state, action);
496
- const outcomes = await this.predictOutcomes(simState);
497
-
498
- const expectedValue = outcomes.reduce((sum, val, i) => {
499
- return sum + val * Math.pow(this.options.gamma, i);
500
- }, 0);
501
-
502
- if (expectedValue > bestOutcome) {
503
- bestOutcome = expectedValue;
504
- bestAction = action;
505
- }
506
- }
507
-
508
- return bestAction;
509
- }
510
-
511
- actionToWeightUpdates(action) {
512
- const updates = [];
513
- let actionIndex = action;
514
-
515
- for (const layer of this.network.weights) {
516
- const layerUpdate = [];
517
- for (let i = 0; i < layer.length; i++) {
518
- const rowUpdate = [];
519
- for (let j = 0; j < layer[i].length; j++) {
520
- const actionValue = actionIndex % this.options.actionSpace;
521
- actionIndex = Math.floor(actionIndex / this.options.actionSpace);
522
- const update =
523
- ((actionValue / (this.options.actionSpace - 1)) * 2 - 1) *
524
- this.options.weightUpdateRange;
525
- rowUpdate.push(update);
526
- }
527
- layerUpdate.push(rowUpdate);
528
- }
529
- updates.push(layerUpdate);
530
- }
531
-
532
- return updates;
533
- }
534
-
535
- async applyAction(action) {
536
- const updates = this.actionToWeightUpdates(action);
537
- for (let i = 0; i < this.network.weights.length; i++) {
538
- for (let j = 0; j < this.network.weights[i].length; j++) {
539
- for (let k = 0; k < this.network.weights[i][j].length; k++) {
540
- this.network.weights[i][j][k] += updates[i][j][k];
541
- }
542
- }
543
- }
544
- }
545
-
546
- calculateReward(oldLoss, newLoss) {
547
- const improvement = oldLoss - newLoss;
548
- const bestReward = newLoss < this.bestLoss ? 1.0 : 0.0;
549
- return improvement + bestReward;
550
- }
551
-
552
- async getActualOutcomes(state, steps) {
553
- const outcomes = [];
554
- let currentState = state;
555
-
556
- for (let i = 0; i < steps; i++) {
557
- const loss = await this.getCurrentLoss();
558
- outcomes.push(loss);
559
- const action = await this.selectAction();
560
- currentState = this.simulateAction(currentState, action);
561
- }
562
-
563
- return outcomes;
564
- }
565
-
566
- async trainOutcomePredictor(experience) {
567
- const { state, nextState } = experience;
568
- const actualOutcomes = await this.getActualOutcomes(
569
- nextState,
570
- this.options.predictionHorizon
571
- );
572
-
573
- const input = [
574
- ...state,
575
- ...this.memoryCells.shortTerm,
576
- ...this.memoryCells.longTerm,
577
- ...this.memoryCells.cellState
578
- ];
579
-
580
- await this.outcomePredictor.train(
581
- [
582
- {
583
- input: input,
584
- output: actualOutcomes
585
- }
586
- ],
587
- {
588
- epochs: 10,
589
- learningRate: this.options.learningRate
590
- }
591
- );
592
- }
593
-
594
- async trainQNetwork(batch) {
595
- for (const experience of batch) {
596
- const { state, action, reward, nextState } = experience;
597
- const currentQ = await this.predictQValue(state, action);
598
-
599
- let maxNextQ = -Infinity;
600
- for (let a = 0; a < this.getActionSpaceSize(); a++) {
601
- const nextQ = await this.predictQValue(nextState, a);
602
- maxNextQ = Math.max(maxNextQ, nextQ);
603
- }
604
-
605
- const targetQ = reward + this.options.gamma * maxNextQ;
606
- const input = [...state, ...this.encodeAction(action)];
607
-
608
- await this.qNetwork.train(
609
- [
610
- {
611
- input: input,
612
- output: [targetQ]
613
- }
614
- ],
615
- {
616
- epochs: 10,
617
- learningRate: this.options.learningRate
618
- }
619
- );
620
- }
621
- }
622
-
623
- async update(currentLoss) {
624
- const state = this.getNetworkState();
625
- const action = await this.selectAction();
626
- await this.applyAction(action);
627
- const nextState = this.getNetworkState();
628
- const newLoss = await this.getCurrentLoss();
629
- const reward = this.calculateReward(currentLoss, newLoss);
630
-
631
- const experience = {
632
- state,
633
- action,
634
- reward,
635
- nextState
636
- };
637
-
638
- this.memory.push(experience);
639
- await this.trainOutcomePredictor(experience);
640
-
641
- if (this.memory.length > this.options.memorySize) {
642
- this.memory.shift();
643
- }
644
-
645
- if (this.memory.length >= this.options.batchSize) {
646
- const batch = [];
647
- for (let i = 0; i < this.options.batchSize; i++) {
648
- const index = Math.floor(Math.random() * this.memory.length);
649
- batch.push(this.memory[index]);
650
- }
651
- await this.trainQNetwork(batch);
652
- }
653
-
654
- if (newLoss < this.bestLoss) {
655
- this.bestLoss = newLoss;
656
- this.bestWeights = this.cloneWeights(this.network.weights);
657
- }
658
-
659
- this.epsilon = Math.max(
660
- this.options.epsilonMin,
661
- this.epsilon * this.options.epsilonDecay
662
- );
663
-
664
- return {
665
- loss: newLoss,
666
- bestLoss: this.bestLoss,
667
- epsilon: this.epsilon
668
- };
669
- }
670
-
671
- cloneWeights(weights) {
672
- return weights.map((layer) => layer.map((row) => [...row]));
673
- }
674
- }
675
- // 🧠 carbono: A Fun and Friendly Neural Network Class 🧠
676
- // This micro-library wraps everything you need to have
677
- // This is the simplest yet functional feedforward mlp in js
678
- class carbono {
679
  constructor(debug = true) {
680
- this.layers = []; // 📚 Stores info about each layer
681
- this.weights = []; // ⚖️ Stores weights for each layer
682
- this.biases = []; // 🔧 Stores biases for each layer
683
- this.activations = []; // 🚀 Stores activation functions for each layer
684
- this.details = {}; // 📊 Stores details about the model
685
- this.debug = debug; // 🐛 Enables or disables debug messages
 
686
  }
687
 
688
- // 🎮 Initialize reinforcement learning module
689
- play(options = {}) {
690
- console.log("Reinforcement Learning Activated");
691
- this.rl = new ReinforcementModule(this, options);
692
- return this.rl;
693
- }
694
-
695
- // 🏗️ Add a new layer to the neural network
696
- layer(inputSize, outputSize, activation = "tanh") {
697
- // 🧱 Store layer information
698
  this.layers.push({
699
  inputSize,
700
  outputSize,
701
  activation
702
  });
703
- // 🔍 Check if the new layer's input size matches the previous layer's output size
704
  if (this.weights.length > 0) {
705
- const lastLayerOutputSize = this.layers[this.layers.length - 2]
706
- .outputSize;
707
  if (inputSize !== lastLayerOutputSize) {
708
- throw new Error(
709
- "Oops! The input size of the new layer must match the output size of the previous layer."
710
- );
711
  }
712
  }
713
- // 🎲 Initialize weights using Xavier/Glorot initialization
714
  const weights = [];
715
  for (let i = 0; i < outputSize; i++) {
716
  const row = [];
717
  for (let j = 0; j < inputSize; j++) {
718
- row.push(
719
- (Math.random() - 0.5) * 2 * Math.sqrt(6 / (inputSize + outputSize))
720
- );
721
  }
722
  weights.push(row);
723
  }
724
  this.weights.push(weights);
725
- // 🎚️ Initialize biases with small positive values
726
  const biases = Array(outputSize).fill(0.01);
727
  this.biases.push(biases);
728
- // 🚀 Store the activation function for this layer
729
  this.activations.push(activation);
730
  }
731
- // 🧮 Apply the activation function
 
732
  activationFunction(x, activation) {
733
  switch (activation) {
734
- case "tanh":
735
- return Math.tanh(x); // 〰️ Hyperbolic tangent
736
- case "sigmoid":
737
- return 1 / (1 + Math.exp(-x)); // 📈 S-shaped curve
738
- case "relu":
739
- return Math.max(0, x); // 📐 Rectified Linear Unit
740
- case "selu":
741
  const alpha = 1.67326;
742
  const scale = 1.0507;
743
- return x > 0 ? scale * x : scale * alpha * (Math.exp(x) - 1); // 🚀 Scaled Exponential Linear Unit
744
  default:
745
- throw new Error("Whoops! We don't know that activation function.");
746
  }
747
  }
748
- // 📐 Calculate the derivative of the activation function
 
749
  activationDerivative(x, activation) {
750
  switch (activation) {
751
- case "tanh":
752
  return 1 - Math.pow(Math.tanh(x), 2);
753
- case "sigmoid":
754
  const sigmoid = 1 / (1 + Math.exp(-x));
755
  return sigmoid * (1 - sigmoid);
756
- case "relu":
757
  return x > 0 ? 1 : 0;
758
- case "selu":
759
  const alpha = 1.67326;
760
  const scale = 1.0507;
761
  return x > 0 ? scale : scale * alpha * Math.exp(x);
762
  default:
763
- throw new Error(
764
- "Oops! We don't know the derivative of that activation function."
765
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
766
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  }
768
- // 🏋️‍♀️ Train the neural network
 
769
  async train(trainSet, options = {}) {
770
- // 🎛️ Set up training options with default values
771
  const {
772
- epochs = 200, // 🔄 Number of times to go through the entire dataset
773
- learningRate = 0.212, // 📏 How big of steps to take when adjusting weights
774
- batchSize = 16, // 📦 Number of samples to process before updating weights
775
- printEveryEpochs = 100, // 🖨️ How often to print progress
776
- earlyStopThreshold = 1e-6, // 🛑 When to stop if the error is small enough
777
- testSet = null, // 🧪 Optional test set for evaluation
778
- callback = null // 📡 Callback function for real-time updates
779
  } = options;
780
- const start = Date.now(); // ⏱️ Start the timer
781
- // 🛡️ Make sure batch size is at least 2
782
  if (batchSize < 1) batchSize = 2;
783
- // 🏗️ Automatically create layers if none exist
784
  if (this.layers.length === 0) {
785
  const numInputs = trainSet[0].input.length;
786
- this.layer(numInputs, numInputs, "tanh");
787
- this.layer(numInputs, 1, "tanh");
788
  }
789
  let lastTrainLoss = 0;
790
  let lastTestLoss = null;
791
- // 🔄 Main training loop
 
 
 
 
792
  for (let epoch = 0; epoch < epochs; epoch++) {
793
  let trainError = 0;
794
- // 📦 Process data in batches
795
  for (let b = 0; b < trainSet.length; b += batchSize) {
796
  const batch = trainSet.slice(b, b + batchSize);
797
  let batchError = 0;
798
- // 🧠 Forward pass and backward pass for each item in the batch
799
  for (const data of batch) {
800
- // 🏃‍♂️ Forward pass
801
  const layerInputs = [data.input];
802
  for (let i = 0; i < this.weights.length; i++) {
803
  const inputs = layerInputs[i];
@@ -815,7 +499,6 @@ class carbono {
815
  }
816
  layerInputs.push(outputs);
817
  }
818
- // 🔙 Backward pass
819
  const outputLayerIndex = this.weights.length - 1;
820
  const outputLayerInputs = layerInputs[layerInputs.length - 1];
821
  const outputErrors = [];
@@ -835,17 +518,10 @@ class carbono {
835
  for (let k = 0; k < this.layers[i + 1].outputSize; k++) {
836
  error += nextLayerErrors[k] * nextLayerWeights[k][j];
837
  }
838
- errors.push(
839
- error *
840
- this.activationDerivative(
841
- currentLayerInputs[j],
842
- currentActivation
843
- )
844
- );
845
  }
846
  layerErrors.unshift(errors);
847
  }
848
- // 🔧 Update weights and biases
849
  for (let i = 0; i < this.weights.length; i++) {
850
  const inputs = layerInputs[i];
851
  const errors = layerErrors[i];
@@ -859,16 +535,11 @@ class carbono {
859
  biases[j] += learningRate * errors[j];
860
  }
861
  }
862
- batchError += Math.abs(outputErrors[0]); // Assuming binary output
863
  }
864
  trainError += batchError;
865
  }
866
  lastTrainLoss = trainError / trainSet.length;
867
- // 🎮 Apply reinforcement learning if initialized
868
- if (this.rl) {
869
- this.rl.update(lastTrainLoss);
870
- }
871
- // 🧪 Evaluate on test set if provided
872
  if (testSet) {
873
  let testError = 0;
874
  for (const data of testSet) {
@@ -877,44 +548,38 @@ class carbono {
877
  }
878
  lastTestLoss = testError / testSet.length;
879
  }
880
- // 📢 Print progress if needed
 
 
 
 
 
 
 
 
881
  if ((epoch + 1) % printEveryEpochs === 0 && this.debug === true) {
882
- console.log(
883
- `Epoch ${epoch + 1}, Train Loss: ${lastTrainLoss.toFixed(6)}${
884
- testSet ? `, Test Loss: ${lastTestLoss.toFixed(6)}` : ""
885
- }`
886
- );
887
  }
888
- // 📡 Call the callback function with current progress
889
  if (callback) {
890
- await callback(epoch + 1, lastTrainLoss, lastTestLoss);
891
  }
892
- // Add a small delay to prevent UI freezing
893
- await new Promise((resolve) => setTimeout(resolve, 0));
894
- // 🛑 Check for early stopping
895
  if (lastTrainLoss < earlyStopThreshold) {
896
- console.log(
897
- `We stopped at epoch ${
898
- epoch + 1
899
- } with train loss: ${lastTrainLoss.toFixed(6)}${
900
- testSet ? ` and test loss: ${lastTestLoss.toFixed(6)}` : ""
901
- }`
902
- );
903
  break;
904
  }
905
  }
906
- const end = Date.now(); // ⏱️ Stop the timer
907
- // 🧮 Calculate total number of parameters
908
  let totalParams = 0;
909
  for (let i = 0; i < this.weights.length; i++) {
910
  const weightLayer = this.weights[i];
911
  const biasLayer = this.biases[i];
912
  totalParams += weightLayer.flat().length + biasLayer.length;
913
  }
914
- // 📊 Create a summary of the training
915
  const trainingSummary = {
916
  trainLoss: lastTrainLoss,
917
  testLoss: lastTestLoss,
 
918
  parameters: totalParams,
919
  training: {
920
  time: end - start,
@@ -922,7 +587,7 @@ class carbono {
922
  learningRate,
923
  batchSize
924
  },
925
- layers: this.layers.map((layer) => ({
926
  inputSize: layer.inputSize,
927
  outputSize: layer.outputSize,
928
  activation: layer.activation
@@ -931,11 +596,12 @@ class carbono {
931
  this.details = trainingSummary;
932
  return trainingSummary;
933
  }
934
- // 🔮 Use the trained network to make predictions
 
935
  predict(input) {
936
  let layerInput = input;
937
- const allActivations = [input]; // Track all activations through layers
938
- const allRawValues = []; // Track pre-activation values
939
  for (let i = 0; i < this.weights.length; i++) {
940
  const weights = this.weights[i];
941
  const biases = this.biases[i];
@@ -955,13 +621,13 @@ class carbono {
955
  allActivations.push(layerOutput);
956
  layerInput = layerOutput;
957
  }
958
- // Store last activation values for visualization
959
  this.lastActivations = allActivations;
960
  this.lastRawValues = allRawValues;
961
  return layerInput;
962
  }
963
- // 💾 Save the model to a file
964
- save(name = "model") {
 
965
  const data = {
966
  weights: this.weights,
967
  biases: this.biases,
@@ -970,16 +636,17 @@ class carbono {
970
  details: this.details
971
  };
972
  const blob = new Blob([JSON.stringify(data)], {
973
- type: "application/json"
974
  });
975
  const url = URL.createObjectURL(blob);
976
- const a = document.createElement("a");
977
  a.href = url;
978
  a.download = `${name}.json`;
979
  a.click();
980
  URL.revokeObjectURL(url);
981
  }
982
- // 📂 Load a saved model from a file
 
983
  load(callback) {
984
  const handleListener = (event) => {
985
  const file = event.target.files[0];
@@ -995,23 +662,23 @@ class carbono {
995
  this.layers = data.layers;
996
  this.details = data.details;
997
  callback();
998
- if (this.debug === true) console.log("Model loaded successfully!");
999
- input.removeEventListener("change", handleListener);
1000
  input.remove();
1001
  } catch (e) {
1002
- input.removeEventListener("change", handleListener);
1003
  input.remove();
1004
- if (this.debug === true) console.error("Failed to load model:", e);
1005
  }
1006
  };
1007
  reader.readAsText(file);
1008
  };
1009
- const input = document.createElement("input");
1010
- input.type = "file";
1011
- input.accept = ".json";
1012
- input.style.opacity = "0";
1013
  document.body.append(input);
1014
- input.addEventListener("change", handleListener.bind(this));
1015
  input.click();
1016
  }
1017
  }
 
318
  </div>
319
 
320
  <script>
321
+ class carbono {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
322
  constructor(debug = true) {
323
+ this.layers = [];
324
+ this.weights = [];
325
+ this.biases = [];
326
+ this.activations = [];
327
+ this.details = {};
328
+ this.debug = debug;
329
+ this.fewShotSamples = [];
330
  }
331
 
332
+ // Add a new layer to the neural network
333
+ layer(inputSize, outputSize, activation = 'tanh') {
 
 
 
 
 
 
 
 
334
  this.layers.push({
335
  inputSize,
336
  outputSize,
337
  activation
338
  });
 
339
  if (this.weights.length > 0) {
340
+ const lastLayerOutputSize = this.layers[this.layers.length - 2].outputSize;
 
341
  if (inputSize !== lastLayerOutputSize) {
342
+ throw new Error('Oops! The input size of the new layer must match the output size of the previous layer.');
 
 
343
  }
344
  }
 
345
  const weights = [];
346
  for (let i = 0; i < outputSize; i++) {
347
  const row = [];
348
  for (let j = 0; j < inputSize; j++) {
349
+ row.push((Math.random() - 0.5) * 2 * Math.sqrt(6 / (inputSize + outputSize)));
 
 
350
  }
351
  weights.push(row);
352
  }
353
  this.weights.push(weights);
 
354
  const biases = Array(outputSize).fill(0.01);
355
  this.biases.push(biases);
 
356
  this.activations.push(activation);
357
  }
358
+
359
+ // Apply the activation function
360
  activationFunction(x, activation) {
361
  switch (activation) {
362
+ case 'tanh':
363
+ return Math.tanh(x);
364
+ case 'sigmoid':
365
+ return 1 / (1 + Math.exp(-x));
366
+ case 'relu':
367
+ return Math.max(0, x);
368
+ case 'selu':
369
  const alpha = 1.67326;
370
  const scale = 1.0507;
371
+ return x > 0 ? scale * x : scale * alpha * (Math.exp(x) - 1);
372
  default:
373
+ throw new Error('Whoops! We don\'t know that activation function.');
374
  }
375
  }
376
+
377
+ // Calculate the derivative of the activation function
378
  activationDerivative(x, activation) {
379
  switch (activation) {
380
+ case 'tanh':
381
  return 1 - Math.pow(Math.tanh(x), 2);
382
+ case 'sigmoid':
383
  const sigmoid = 1 / (1 + Math.exp(-x));
384
  return sigmoid * (1 - sigmoid);
385
+ case 'relu':
386
  return x > 0 ? 1 : 0;
387
+ case 'selu':
388
  const alpha = 1.67326;
389
  const scale = 1.0507;
390
  return x > 0 ? scale : scale * alpha * Math.exp(x);
391
  default:
392
+ throw new Error('Oops! We don\'t know the derivative of that activation function.');
393
+ }
394
+ }
395
+
396
+ // Generate few-shot samples
397
+ generateFewShotSamples(trainSet, numSamples = 10) {
398
+ const fewShotSamples = [];
399
+ for (let i = 0; i < numSamples; i++) {
400
+ const randomIndex = Math.floor(Math.random() * trainSet.length);
401
+ fewShotSamples.push(trainSet[randomIndex]);
402
+ }
403
+ return fewShotSamples;
404
+ }
405
+
406
+ // Positional Encoding
407
+ positionalEncoding(input, maxLen) {
408
+ const pe = new Array(maxLen).fill(0).map((_, pos) => {
409
+ return new Array(input[0].length).fill(0).map((_, i) => {
410
+ const angle = pos / Math.pow(10000, 2 * i / input[0].length);
411
+ return pos % 2 === 0 ? Math.sin(angle) : Math.cos(angle);
412
+ });
413
+ });
414
+ return input.map((seq, idx) => seq.map((val, i) => val + pe[idx][i]));
415
+ }
416
+
417
+ // Simplified Multi-Head Self-Attention
418
+ multiHeadSelfAttention(input, numHeads = 2) {
419
+ const headSize = input[0].length / numHeads;
420
+ const heads = new Array(numHeads).fill(0).map(() => new Array(input.length).fill(0).map(() => new Array(headSize).fill(0)));
421
+ for (let h = 0; h < numHeads; h++) {
422
+ for (let i = 0; i < input.length; i++) {
423
+ for (let j = 0; j < headSize; j++) {
424
+ heads[h][i][j] = input[i][h * headSize + j];
425
+ }
426
+ }
427
  }
428
+ const attentionScores = new Array(numHeads).fill(0).map(() => new Array(input.length).fill(0).map(() => new Array(input.length).fill(0)));
429
+ for (let h = 0; h < numHeads; h++) {
430
+ for (let i = 0; i < input.length; i++) {
431
+ for (let j = 0; j < input.length; j++) {
432
+ let score = 0;
433
+ for (let k = 0; k < headSize; k++) {
434
+ score += heads[h][i][k] * heads[h][j][k];
435
+ }
436
+ attentionScores[h][i][j] = score;
437
+ }
438
+ }
439
+ }
440
+ const attentionWeights = attentionScores.map(head => head.map(row => row.map(score => Math.exp(score) / row.reduce((sum, s) => sum + Math.exp(s), 0))));
441
+ const output = new Array(input.length).fill(0).map(() => new Array(input[0].length).fill(0));
442
+ for (let h = 0; h < numHeads; h++) {
443
+ for (let i = 0; i < input.length; i++) {
444
+ for (let j = 0; j < headSize; j++) {
445
+ for (let k = 0; k < input.length; k++) {
446
+ output[i][h * headSize + j] += attentionWeights[h][i][k] * heads[h][k][j];
447
+ }
448
+ }
449
+ }
450
+ }
451
+ return output;
452
  }
453
+
454
+ // Train the neural network
455
  async train(trainSet, options = {}) {
 
456
  const {
457
+ epochs = 200,
458
+ learningRate = 0.212,
459
+ batchSize = 16,
460
+ printEveryEpochs = 100,
461
+ earlyStopThreshold = 1e-6,
462
+ testSet = null,
463
+ callback = null
464
  } = options;
465
+ const start = Date.now();
 
466
  if (batchSize < 1) batchSize = 2;
 
467
  if (this.layers.length === 0) {
468
  const numInputs = trainSet[0].input.length;
469
+ this.layer(numInputs, numInputs, 'tanh');
470
+ this.layer(numInputs, 1, 'tanh');
471
  }
472
  let lastTrainLoss = 0;
473
  let lastTestLoss = null;
474
+ let lastFewShotLoss = null;
475
+
476
+ // Generate few-shot samples
477
+ this.fewShotSamples = this.generateFewShotSamples(trainSet);
478
+
479
  for (let epoch = 0; epoch < epochs; epoch++) {
480
  let trainError = 0;
 
481
  for (let b = 0; b < trainSet.length; b += batchSize) {
482
  const batch = trainSet.slice(b, b + batchSize);
483
  let batchError = 0;
 
484
  for (const data of batch) {
 
485
  const layerInputs = [data.input];
486
  for (let i = 0; i < this.weights.length; i++) {
487
  const inputs = layerInputs[i];
 
499
  }
500
  layerInputs.push(outputs);
501
  }
 
502
  const outputLayerIndex = this.weights.length - 1;
503
  const outputLayerInputs = layerInputs[layerInputs.length - 1];
504
  const outputErrors = [];
 
518
  for (let k = 0; k < this.layers[i + 1].outputSize; k++) {
519
  error += nextLayerErrors[k] * nextLayerWeights[k][j];
520
  }
521
+ errors.push(error * this.activationDerivative(currentLayerInputs[j], currentActivation));
 
 
 
 
 
 
522
  }
523
  layerErrors.unshift(errors);
524
  }
 
525
  for (let i = 0; i < this.weights.length; i++) {
526
  const inputs = layerInputs[i];
527
  const errors = layerErrors[i];
 
535
  biases[j] += learningRate * errors[j];
536
  }
537
  }
538
+ batchError += Math.abs(outputErrors[0]);
539
  }
540
  trainError += batchError;
541
  }
542
  lastTrainLoss = trainError / trainSet.length;
 
 
 
 
 
543
  if (testSet) {
544
  let testError = 0;
545
  for (const data of testSet) {
 
548
  }
549
  lastTestLoss = testError / testSet.length;
550
  }
551
+
552
+ // Evaluate on few-shot samples
553
+ let fewShotError = 0;
554
+ for (const data of this.fewShotSamples) {
555
+ const prediction = this.predict(data.input);
556
+ fewShotError += Math.abs(data.output[0] - prediction[0]);
557
+ }
558
+ lastFewShotLoss = fewShotError / this.fewShotSamples.length;
559
+
560
  if ((epoch + 1) % printEveryEpochs === 0 && this.debug === true) {
561
+ console.log(`Epoch ${epoch + 1}, Train Loss: ${lastTrainLoss.toFixed(6)}${testSet ? `, Test Loss: ${lastTestLoss.toFixed(6)}` : ''}, Few-Shot Loss: ${lastFewShotLoss.toFixed(6)}`);
 
 
 
 
562
  }
 
563
  if (callback) {
564
+ await callback(epoch + 1, lastTrainLoss, lastTestLoss, lastFewShotLoss);
565
  }
566
+ await new Promise(resolve => setTimeout(resolve, 0));
 
 
567
  if (lastTrainLoss < earlyStopThreshold) {
568
+ console.log(`We stopped at epoch ${epoch + 1} with train loss: ${lastTrainLoss.toFixed(6)}${testSet ? ` and test loss: ${lastTestLoss.toFixed(6)}` : ''} and few-shot loss: ${lastFewShotLoss.toFixed(6)}`);
 
 
 
 
 
 
569
  break;
570
  }
571
  }
572
+ const end = Date.now();
 
573
  let totalParams = 0;
574
  for (let i = 0; i < this.weights.length; i++) {
575
  const weightLayer = this.weights[i];
576
  const biasLayer = this.biases[i];
577
  totalParams += weightLayer.flat().length + biasLayer.length;
578
  }
 
579
  const trainingSummary = {
580
  trainLoss: lastTrainLoss,
581
  testLoss: lastTestLoss,
582
+ fewShotLoss: lastFewShotLoss,
583
  parameters: totalParams,
584
  training: {
585
  time: end - start,
 
587
  learningRate,
588
  batchSize
589
  },
590
+ layers: this.layers.map(layer => ({
591
  inputSize: layer.inputSize,
592
  outputSize: layer.outputSize,
593
  activation: layer.activation
 
596
  this.details = trainingSummary;
597
  return trainingSummary;
598
  }
599
+
600
+ // Use the trained network to make predictions
601
  predict(input) {
602
  let layerInput = input;
603
+ const allActivations = [input];
604
+ const allRawValues = [];
605
  for (let i = 0; i < this.weights.length; i++) {
606
  const weights = this.weights[i];
607
  const biases = this.biases[i];
 
621
  allActivations.push(layerOutput);
622
  layerInput = layerOutput;
623
  }
 
624
  this.lastActivations = allActivations;
625
  this.lastRawValues = allRawValues;
626
  return layerInput;
627
  }
628
+
629
+ // Save the model to a file
630
+ save(name = 'model') {
631
  const data = {
632
  weights: this.weights,
633
  biases: this.biases,
 
636
  details: this.details
637
  };
638
  const blob = new Blob([JSON.stringify(data)], {
639
+ type: 'application/json'
640
  });
641
  const url = URL.createObjectURL(blob);
642
+ const a = document.createElement('a');
643
  a.href = url;
644
  a.download = `${name}.json`;
645
  a.click();
646
  URL.revokeObjectURL(url);
647
  }
648
+
649
+ // Load a saved model from a file
650
  load(callback) {
651
  const handleListener = (event) => {
652
  const file = event.target.files[0];
 
662
  this.layers = data.layers;
663
  this.details = data.details;
664
  callback();
665
+ if (this.debug === true) console.log('Model loaded successfully!');
666
+ input.removeEventListener('change', handleListener);
667
  input.remove();
668
  } catch (e) {
669
+ input.removeEventListener('change', handleListener);
670
  input.remove();
671
+ if (this.debug === true) console.error('Failed to load model:', e);
672
  }
673
  };
674
  reader.readAsText(file);
675
  };
676
+ const input = document.createElement('input');
677
+ input.type = 'file';
678
+ input.accept = '.json';
679
+ input.style.opacity = '0';
680
  document.body.append(input);
681
+ input.addEventListener('change', handleListener.bind(this));
682
  input.click();
683
  }
684
  }