www.xbdev.net
xbdev - software development
Friday February 6, 2026
Home | Contact | Support | WebGPU Graphics and Compute ... | Neural Networks and WebGPU Compute.. Learning from Data.....
     
 

Neural Networks and WebGPU Compute..

Learning from Data.....

 


Generic Compute Neural Network (Modular)


Step back and shift the neural network to a set of modular components - so it can be used for different test projects.

Encapsulating the functionality in component - lets you pass configuration information - either from a config file or directly using accessor functions (e.g., reset weights or load them from files, train/test using gpu or cpu, ..).

When you initialize the network you can pass options to decide if you use WebGPU, native JavaScript or some other implementation (e.g., WebGL).

As you develop and extend the implementation with enhancements - novel and experimental version to take the training algorithm further - it won't break things (set flags if they should be enabled/disabled).

Also once you've trained your network - you might want to save it to a config file which can be loaded when needed instead of training it from the start.







Complete Code



/*
    Neural networks - tested with xor and back propagation
    
    Array 'Blocks' for the Neural Network Data (layer outputs, weights, errors, ..)
    
    CPU and GPU VERSION - Compares/Checks Results
    
    Modular funtions - load/save/reset/ (both cpu/gpu) - compare
*/

console.log('xnet.js');


let xlayers       = [2,3,1];
let xbuild        'cpu';
let xiterations   1;
let xlearningrate 0.1;


xinitialize = ( arg = { layers:[1,2,1], build:'cpu'iterations:10learningrate:0.2 } ) => {};
xactivate   async ip )  => { };
xpropagate  async op )  => { };


//--------------------------------------------------------------------------------------

const config = {
    
layers: [232], // Number of neurons per layer (input layer, hidden layers, output layer)
  
    
weights: [
      
// Weights between layer 0 and layer 1
      
[
        [
0.5, -0.20.1], // Weights from neuron 0 in layer 0 to neurons in layer 1
        
[0.30.8, -0.5],
      ],
      
// Weights between layer 1 and layer 2
      
[
        [
0.1, -0.4], // Weights from neuron 0 in layer 1 to neurons in layer 2
        
[-0.70.8],
        [
0.6,  0.2],
      ]
    ],
    
biases: [
      
// Biases for layer 0
      
[0.00.0],
      
// Biases for layer 1
      
[0.1, -0.20.3],
      
// Biases for layer 2
      
[0.5, -0.1]
    ],
    
// test data to check the network is connected correctly with coefficients
    
check: { 
      
input:  [ 0],
      
output: [ 0.59339278936386110.5663766860961914 ]
    }
};


//--------------------------------------------------------------------------------------


let   LEARNING_RATE 0.2;
const 
VARIANCE_W    0.5;
const 
randomUniform = (minmax) => Math.random() * (max min) + min;

const 
ru = ()=>{ return randomUniform(-VARIANCE_WVARIANCE_W); }

let   layers  = [ 23];
let   maxLayerSize = [...layers].sort( (a,b)=>b-)[0];

const 
xordataset = [  { inputs: [0,0], outputs: [0] },
                      { 
inputs: [0,1], outputs: [1] },
                      { 
inputs: [1,0], outputs: [1] },
                      { 
inputs: [1,1], outputs: [0] }  ];

console.assertxordataset[0].outputs.length == layerslayers.length-] );

let   weights  = Array( layers.length maxLayerSize maxLayerSize ).fill);
let   biases   = Array( layers.length maxLayerSize ).fill(0);
let   loutputs = Array( layers.length maxLayerSize ).fill(0);
let   errors   = Array( layers.length maxLayerSize ).fill(0);

let   MAX_NEURONS_PER_LAYER maxLayerSize;
let   NUM_LAYERS            layers.length;

//--------------------------------------------------------------------------------------

// Default is random weights and biases - overridden later on by config file if loaded
const initializeWeightsandBiases = () => {
      
console.log('initializeWeightsandBiases');
    for (
let i=0i<layers.length-1i++)
    {
       for (
let k=0k<layers[i]; i++)
       {
            for (
let g=0g<layers[i+1]; g++)
            {
                
setWeightikru() );
            }
       }
    }
   
    for (
let i=0i<layers.length-1i++)
    {
       for (
let k=0k<layers[i]; i++)
       {
             
setBiasik0.0 );
       }
    }
}
initializeWeightsandBiases();

//--------------------------------------------------------------------------------------

function getBias(layerneuron) {
  return 
biases[layer MAX_NEURONS_PER_LAYER neuron];
}

function 
setBias(layerneuronvalue) {
  
biases[layer MAX_NEURONS_PER_LAYER neuron] = value;
}

function 
setOutput(layerneuronvalue) {
  
loutputs[layer MAX_NEURONS_PER_LAYER neuron] = value;
}

function 
getOutput(layerneuron) {
  return 
loutputs[layer MAX_NEURONS_PER_LAYER neuron];
}

function 
getWeight(layerfromNeurontoNeuron) {
  return 
weights[layer MAX_NEURONS_PER_LAYER MAX_NEURONS_PER_LAYER
                
fromNeuron MAX_NEURONS_PER_LAYER
                
toNeuron];
}

function 
setWeight(layerfromNeurontoNeuronvalue) {
  
weights[layer MAX_NEURONS_PER_LAYER MAX_NEURONS_PER_LAYER
         
fromNeuron MAX_NEURONS_PER_LAYER
         
toNeuron] = value;
}

function 
setError(layerneuronvalue) {
  
errors[layer MAX_NEURONS_PER_LAYER neuron] = value;
}

function 
getError(layerneuron) {
  return 
errors[layer MAX_NEURONS_PER_LAYER neuron];
}


//--------------------------------------------------------------------------------------

function parseConfigjson )
{
    
//if ( typeof(config.weights) != 'undefined' )
    
for (let i=0i<config.weights.lengthi++)
    {
       const 
layer0 i;
       const 
layer1 i+1;

       const 
layer0N config.weights[i].length;
       const 
layer1N config.weights[i][0].length;

       
console.assertlayer0N == config.layers[i] );
       
console.assertlayer1N == config.layers[i+1] );

       for (
let n0=0n0<layer0Nn0++)
       {
          for (
let n1=0n1<layer1Nn1++)
          {
              const 
weight  config.weights[layer0][n0][n1];
              
setWeightlayer0n0n1weight );
          }
       }
    }

    
// setup biases 
    //if ( typeof(config.biases) != 'undefined' )
    
for (let b=0b<config.biases.lengthb++)
    {
       
// layer
       
for (let i=0i<config.biases[b].lengthi++)
       { 
           
// each neuron
           
const bias config.biases[b][i];
           
setBiasbibias );
       }
    }
}

//--------------------------------------------------------------------------------------

function loadConfig()
{
    
let inputdiv    document.createElement('input');
    
inputdiv.type   'file';
    
inputdiv.accept 'json/json';
    
inputdiv.onchange = function()
    {
          const 
fileList this.files
          
let reader = new FileReader(); // no arguments

          
reader.readAsTextthis.files[0] );

          
reader.onload = function() {

        
parseConfigreader.result );
      };
    }
    
inputdiv.click();
}

//--------------------------------------------------------------------------------------

function saveConfig()
{
    
// Save config network (layout, weights, biases, ..)

    
let newconfig = {
      
layers  : [],
      
weights : [],
      
biases  : [],
      
check   : {
              
input : [],
              
output: [] }
    };

    
// layers
    
for (let i=0i<layers.lengthi++)
    {
       
newconfig.layers.pushlayers[i] );
    }

    
// weights
    
for (let i=0i<layers.length-1i++)
    {
        
let layerweights = [];
        for (
let n0=0n0<layers[i]; n0++)
        {
          
let weights = [];
          for (
let n1=0n1<layers[i+1]; n1++)
          {
                 const 
weight getWeightin0n1 );
              
weights.pushweight );
          }
          
layerweights.pushweights );
        }
        
newconfig.weights.pushlayerweights );
    }

    
// biases
    
for (let i=0i<layers.lengthi++)
    {
        
let layerbiases = [];
        for (
let n0=0n0<layers[i]; n0++)
        {
            const 
bias getBiasin0 );
            
layerbiases.pushbias );
        }
        
newconfig.biases.pushlayerbiases );
    }

    
//newconfig.check.input  = dataset[1].inputs;
    //newconfig.check.output = activate( dataset[1].inputs );

    // download dialog
    
let json JSON.stringifynewconfig );
    
let blob2 = new Blob([json], {type"text/txt"});
    
let url2 window.URL.createObjectURL(blob2);
    
let link document.createElement('a');
    
document.body.appendChildlink );
    
link.innerHTML 'download config';
    
link.download'nnconfig.json';
    
link.href url2 ;
    
link.click();
}

//--------------------------------------------------------------------------------------

const sigmoid = (x) => 1.0 / (1.0 Math.exp(-x));

const 
sigmoidDerivative = (x) => * (x);

const 
relu = (x) =>  { return Math.max(0.0x); }

const 
reluDerivative = (x) => {  if (0.0) { return 1.0; } return 0.0; }

const 
leakyRelu = (xalpha 0.01) => { return alpha x; }

const 
leakyReluDerivative = (xalpha 0.01) => { return alpha; }


const 
activate = (iin) => {
      
//console.assert( iin.length == outputs[ 0 ].length );
      //console.assert( weights[0].length    == layers[0] );
      //console.assert( weights[0][0].length == layers[1] );
      //console.assert( weights[1].length    == layers[1] );
    //console.assert( weights[1][0].length == layers[2] );
  
    
for (let i=0i<NUM_LAYERSi++)
    {
          if ( 
i==)
        {
              for (
let k=0k<iin.lengthk++)
            {
                
setOutput(0kiin] );
            }
        }
        else
        {
              for (
let k=0k<layers[i]; k++)
            {
                  var 
sum 0.0;
                  for (
let b=0b<layers[i-1]; b++)
                {
                      
sum += getOutputi-1) * getWeighti-1b);
                }
                
setOutputik,  sigmoidsum getBiasi) ) );
            }
            
        }
    }

      
let out = [];
    for (
let k=0k<layersNUM_LAYERS-]; k++)
    {
        
out.pushgetOutputNUM_LAYERS-1) );
    }
    return 
out;
};

//--------------------------------------------------------------------------------------

const propagate = ( target ) => {

    for (
let i=NUM_LAYERS-1i>0i--)
    {
          for (
let k=0k<layers[i]; k++)
        {
              if ( 
i==NUM_LAYERS-)
            {
                  
let error = ( target[k] - getOutputi) ) * sigmoidDerivativegetOutputi,) );
                
setErrorikerror );
            }
            else
            {
                  
setErrorik0.0 ); 
                  for (
let g=0g<layers[i+1]; g++)
                {
                      
let error getErrori+1) * getWeighti,k,) * sigmoidDerivativegetOutput(i,k) );
                    
setErrorikerror );
                }
            }
        }
    }
      
    for (
let i=0i<NUM_LAYERSi++)
    {
          for (
let k=0k<layers[i]; k++)
        {
            if ( 
NUM_LAYERS-) {
              for (
let g=0g<layers[i+1]; g++)
            {
                  var 
weight getWeightik);
                  
weight += LEARNING_RATE getOutput(i,k) * getError(i+1,g);
                  
setWeightikgweight ); 
            }
            }
          
            
let bias getBiasi,);
            
bias += LEARNING_RATE getErrori);
            
setBiasikbias );
        }
    }
};

//--------------------------------------------------------------------------------------

console.log( new Date() );

//--------------------------------------------------------------------------------------

function outputJSNetwork()
{
    
let cost 0;
    for (
let j 0xordataset.lengthj++) {
      
let o activate(  xordataset[j].inputs );
      for (
let b=0b<xordataset[j].outputs.lengthb++)
      {
        
cost += Math.powxordataset[j].outputs[b] - o[b], 2);
      }
    }
    
cost /= 4;
    
console.log(`total error (no training) mean squared error: ${cost}`);
  
    for (
let i 0xordataset.lengthi++) 
    {
        const 
result activatexordataset[i].inputs );

        
console.log(`for input ${xordataset[i].inputs} expected ${xordataset[i].outputs} predicted ${result[0].toFixed(4)} which is ${Math.round(result[0]) === xordataset[i].outputs[0] ? "correct" "incorrect"}`);
    }
  
}

//--------------------------------------------------------------------------------------


function trainJSNetwork()
{
    
console.log(' ----------TRAIN WITH GPU VERSION------------- ' );  
    
// - test the neural network using iteration loop - xor dataset

    
for (let epoch 0epoch <= 10000epoch++) {
        
let indexes = Array.from(Array( xordataset.length ).keys());
        
indexes.sort(() => Math.random() - 0.5);
        for (
let j of indexes) {
            
activatexordataset[j].inputs ); 
            
propagatexordataset[j].outputsLEARNING_RATE);
        }

        if (
epoch 1000 === 0) {
            
let cost 0;
            for (
let j 0xordataset.lengthj++) {
                
let o activate(  xordataset[j].inputs );
                for (
let b=0b<xordataset[j].outputs.lengthb++)
                {
                    
cost += Math.powxordataset[j].outputs[b] - o[b], 2);
                }
            }
            
cost /= 4;
            
console.log(`epoch ${epoch} mean squared error: ${cost}`);
        }
    }

    for (
let i 0xordataset.lengthi++) 
    {
        const 
result activatexordataset[i].inputs );

        
console.log(`for input ${xordataset[i].inputs} expected ${xordataset[i].outputs} predicted ${result[0].toFixed(4)} which is ${Math.round(result[0]) === xordataset[i].outputs[0] ? "correct" "incorrect"}`);
    }

}


//--------------------------------------------------------------------------------------

const but = ( s) =>
{
  
let butEl document.createElement('button');
  
butEl.onclick = () => { n(); }
  
butEl.innerHTML s;
  
document.body.appendChildbutEl );
  
document.body.appendChilddocument.createElement('br') );
}

//---------------------------------------------------------------------------


const adapter await navigator.gpu.requestAdapter();
const 
device  await adapter.requestDevice();

//---------------------------------------------------------------------------



async function runComputePipeline(shaderCodebindingsworkGroupCountentryFunction='main') {
    const 
shaderModule device.createShaderModule({ codeshaderCode });
  
    
let entries = [];
    for (
let n=0n<bindings.lengthn++) {
       
entries.push( {bindingbindings[n].bindingvisibilityGPUShaderStage.COMPUTEbuffer: {type"storage"} } );
    }
    const 
bindGroupLayout device.createBindGroupLayout( { "entries"entries } );
      
/*
    const bindGroupLayout = device.createBindGroupLayout({
        entries: [ {binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: {type: "storage"}  },
                   {binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: {type: "storage"}  },
                   {binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: {type: "storage"}  },
                   {binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: {type: "storage"}  } 
                 ]
      });
    */
  
    
const pipeline device.createComputePipeline({
      
layoutdevice.createPipelineLayout({bindGroupLayouts: [bindGroupLayout]}),
      
compute: { moduleshaderModuleentryPointentryFunction },
    });

    const 
bindGroup device.createBindGroup({ layoutbindGroupLayoutentriesbindings });

    const 
commandEncoder device.createCommandEncoder();
    const 
passEncoder commandEncoder.beginComputePass();
    
passEncoder.setPipeline(pipeline);
    
passEncoder.setBindGroup(0bindGroup);
    
passEncoder.dispatchWorkgroups(workGroupCount);
    
passEncoder.end();

    const 
commandBuffer commandEncoder.finish();
    
device.queue.submit([commandBuffer]);
    
await device.queue.onSubmittedWorkDone();
  
    
// Return pipeline/group 
    
return { pipeline:pipelinebindGroup:bindGroupworkGroupCount:workGroupCount };
}

function 
createBufferdatausage ) {
    const 
buffer device.createBuffer({
      
sizedata.byteLength,
      
usageusage GPUBufferUsage.COPY_DST,
      
mappedAtCreationtrue,
    });
    new 
data.constructor(buffer.getMappedRange()).set(data);
    
buffer.unmap();
    return 
buffer;
}

let stConfig = {
    
MAX_NEURONS_PER_LAYER maxLayerSize,  // u32,
    
NUM_LAYERS            NUM_LAYERS,    // u32,
    
LEARNING_RATE         LEARNING_RATE*1000000// f32, // 0.1, 0.2, ...
    
ACTIVATION_TYPE       0              // u32  // enum - 0-sig, 1-relu, 2-reluleaky, ...
}

let configBuffer    createBuffer(new Uint32ArrayObject.values(stConfig) ), GPUBufferUsage.STORAGE );
let layersBuffer    createBuffer(new Uint32Arraylayers ), GPUBufferUsage.STORAGE );
let inputsBuffer    createBuffer(new Float32Arraylayers[0]  ), GPUBufferUsage.STORAGE );
let resultsBuffer   createBuffer(new Float32Arraylayers[layers.length-1] ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC );
let expectedBuffer  createBuffer(new Float32Arraylayers[layers.length-1] ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC );
let weightsBuffer   createBuffer(new Float32Arrayweights.flat(4) ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
let biasesBuffer    createBuffer(new Float32Arraybiases.flat(4)  ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
let outputsBuffer   createBuffer(new Float32Arrayloutputs.flat(4) ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
let errorsBuffer    createBuffer(new Float32Arrayerrors.flat(4) ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);


//---------------------------------------------------------------------------



// Shaders
const forwardShaderCode = `
//const MAX_NEURONS_PER_LAYER = 
${maxLayerSize};
//const NUM_LAYERS            = 
${NUM_LAYERS};

struct stConfig {
    MAX_NEURONS_PER_LAYER : u32,
    NUM_LAYERS            : u32,
    LEARNING_RATE         : u32, // 0.1, 0.2, ...
    ACTIVATION_TYPE       : u32  // enum 0-sig, 1-relu, 2-reluleaky, ...
}

@group(0) @binding(0) var<storage, read_write> layers       : array<u32>;
@group(0) @binding(1) var<storage, read_write> weights      : array<f32>;
@group(0) @binding(2) var<storage, read_write> biases       : array<f32>;
@group(0) @binding(3) var<storage, read_write> inputs       : array<f32>;
@group(0) @binding(4) var<storage, read_write> results      : array<f32>;
@group(0) @binding(5) var<storage, read_write> outputs      : array<f32>;
@group(0) @binding(6) var<storage, read_write> config       : stConfig;

// Activation function (Sigmoid)
fn sigmoid(x:f32) -> f32 {
  return 1.0 / (1.0 + exp(-x));
}

fn getBias(layer: u32, neuron: u32) -> f32 {
    return biases[ layer * config.MAX_NEURONS_PER_LAYER + neuron ];
}

fn setOutput(layer: u32, neuron: u32, value: f32) {
    outputs[ layer * config.MAX_NEURONS_PER_LAYER + neuron ] = value;
}

fn getOutput(layer: u32, neuron: u32) -> f32 {
    return outputs[ layer * config.MAX_NEURONS_PER_LAYER + neuron ];
}

fn getWeight(layer: u32, fromNeuron: u32, toNeuron: u32 ) -> f32 {
    return weights[ layer * config.MAX_NEURONS_PER_LAYER * config.MAX_NEURONS_PER_LAYER
                   + fromNeuron * config.MAX_NEURONS_PER_LAYER
                   + toNeuron ];
}


@compute @workgroup_size( 1 ) 
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
    // Single threaded version - basic compute testing reconisonse
    
    for (var i:u32=0; i<config.NUM_LAYERS; i++)
    {
          if ( i==0 )
        {
              for (var k:u32=0; k< arrayLength(&inputs); k++)
            {
                setOutput(0, k, inputs[ k ] );
            }
        }
        else
        {
              for (var k:u32=0; k<layers[i]; k++)
            {
                  var sum = 0.0;
                  for (var b:u32=0; b<layers[i-1]; b++)
                {
                      sum += getOutput( i-1, b ) * getWeight( i-1, b, k );
                }
                setOutput( i, k,  sigmoid( sum + getBias( i, k ) ) );
            }
        }
    }
    
    for (var k:u32=0; k< arrayLength(&results); k++)
    {
        results[k] = getOutput( arrayLength(&layers)-1 , k );
    }
}
`;


async function activateGPUinputs )
{
    
device.queue.writeBufferinputsBuffer,     0, new Float32Arrayinputs ) );
    
await device.queue.onSubmittedWorkDone();

    
await runComputePipeline(forwardShaderCode, [
                  { 
binding0resource: { bufferlayersBuffer        } },
                  { 
binding1resource: { bufferweightsBuffer       } },
                  { 
binding2resource: { bufferbiasesBuffer        } },
                  { 
binding3resource: { bufferinputsBuffer        } },
                  { 
binding4resource: { bufferresultsBuffer       } },
                  { 
binding5resource: { bufferoutputsBuffer       } },
                  { 
binding6resource: { bufferconfigBuffer        } },
            ], 
);
  
    
// Retrieve results
    
const readBuffer device.createBuffer({sizeresultsBuffer.sizeusageGPUBufferUsage.COPY_DST GPUBufferUsage.MAP_READ });

    const 
commandEncoder device.createCommandEncoder();
    
commandEncoder.copyBufferToBuffer(resultsBuffer0readBuffer0resultsBuffer.size);
    
device.queue.submit([commandEncoder.finish()]);

    
await readBuffer.mapAsync(GPUMapMode.READ);
    const 
results = Array.from( new Float32Array(readBuffer.getMappedRange()) );
    
readBuffer.unmap();
      return 
results;
}


//---------------------------------------------------------------------------


// Shaders
const backwardShaderCode = `
//const MAX_NEURONS_PER_LAYER = 
${maxLayerSize};
//const NUM_LAYERS            = 
${NUM_LAYERS};
//const LEARNING_RATE:f32     = 
${LEARNING_RATE};

struct stConfig {
    MAX_NEURONS_PER_LAYER : u32,
    NUM_LAYERS            : u32,
    LEARNING_RATE         : u32, // 0.1, 0.2, ...
    ACTIVATION_TYPE       : u32  // enum 0-sig, 1-relu, 2-reluleaky, ...
}

@group(0) @binding(0) var<storage, read_write> layers       : array<u32>;
@group(0) @binding(1) var<storage, read_write> weights      : array<f32>;
@group(0) @binding(2) var<storage, read_write> biases       : array<f32>;
@group(0) @binding(3) var<storage, read_write> inputs       : array<f32>;
@group(0) @binding(4) var<storage, read_write> outputs      : array<f32>;
@group(0) @binding(5) var<storage, read_write> expected     : array<f32>;
@group(0) @binding(6) var<storage, read_write> errors       : array<f32>;
@group(0) @binding(7) var<storage, read_write> config       : stConfig;

// Derivative of sigmoid function
fn sigmoidDerivative(x:f32) -> f32 {
  return x * (1 - x);
}

fn getBias(layer: u32, neuron: u32) -> f32 {
    return biases[ layer * config.MAX_NEURONS_PER_LAYER + neuron ];
}

fn setBias(layer: u32, neuron: u32, value:f32 ) {
    biases[ layer * config.MAX_NEURONS_PER_LAYER + neuron ] = value;
}

fn setOutput(layer: u32, neuron: u32, value: f32) {
    outputs[ layer * config.MAX_NEURONS_PER_LAYER + neuron ] = value;
}

fn getOutput(layer: u32, neuron: u32) -> f32 {
    return outputs[ layer * config.MAX_NEURONS_PER_LAYER + neuron ];
}

fn getWeight(layer: u32, fromNeuron: u32, toNeuron: u32 ) -> f32 {
    return weights[ layer * config.MAX_NEURONS_PER_LAYER * config.MAX_NEURONS_PER_LAYER
                   + fromNeuron * config.MAX_NEURONS_PER_LAYER
                   + toNeuron ];
}

fn setWeight(layer: u32, fromNeuron: u32, toNeuron: u32, value:f32 ) {
    weights[ layer * config.MAX_NEURONS_PER_LAYER * config.MAX_NEURONS_PER_LAYER
                   + fromNeuron * config.MAX_NEURONS_PER_LAYER
                   + toNeuron ] = value;
}

fn setError( layer: u32, neuron: u32, value: f32 ){
    errors[ layer * config.MAX_NEURONS_PER_LAYER + neuron ] = value;
}

fn getError( layer: u32, neuron: u32 ) -> f32 {
    return errors[ layer * config.MAX_NEURONS_PER_LAYER + neuron ];
}


@compute @workgroup_size( 1 )
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) 
{
    // Single threaded - test compute reconisone 
    
    let LEARNING_RATE:f32 = f32( config.LEARNING_RATE )/1000000;
    
    for (var i:u32=config.NUM_LAYERS-1; i>0; i--)
    {
          for (var k:u32=0; k<layers[i]; k++)
        {
              if ( i==config.NUM_LAYERS-1 )
            {
                  let error = ( expected[k] - getOutput( i, k ) ) * sigmoidDerivative( getOutput( i,k ) );
                setError( i, k, error );
            }
            else
            {
                  setError( i, k, 0.0 ); 
                  for (var g:u32=0; g<layers[i+1]; g++)
                {
                      let error = getError( i+1, g ) * getWeight( i,k,g ) * sigmoidDerivative( getOutput(i,k) );
                    setError( i, k, error );
                }
            }
        }
    }

      
    for (var i:u32=0; i<config.NUM_LAYERS; i++)
    {
          for (var k:u32=0; k<layers[i]; k++)
        {
            if ( i < config.NUM_LAYERS-1 ) {
              for (var g:u32=0; g<layers[i+1]; g++)
            {
                  var weight = getWeight( i, k, g );
                  weight += LEARNING_RATE * getOutput(i,k) * getError(i+1,g);
                  setWeight( i, k, g, weight ); 
            } }
          
            var bias = getBias( i,k );
            bias += LEARNING_RATE * getError( i, k );
            setBias( i, k, bias );
        }
    }
}    
`;


async function propagateGPUexpected )
{
    
device.queue.writeBufferexpectedBuffer,   0, new Float32Arrayexpected ) );
    
await device.queue.onSubmittedWorkDone();
  
    
await runComputePipeline(backwardShaderCode, [
            { 
binding0resource: { bufferlayersBuffer       } },
          { 
binding1resource: { bufferweightsBuffer      } },
          { 
binding2resource: { bufferbiasesBuffer       } },
          { 
binding3resource: { bufferinputsBuffer       } },
          { 
binding4resource: { bufferoutputsBuffer      } },
          { 
binding5resource: { bufferexpectedBuffer     } },
          { 
binding6resource: { buffererrorsBuffer       } },
          { 
binding7resource: { bufferconfigBuffer        } },
    ], 
);
}



//---------------------------------------------------------------------------


async function outputGPUNetwork()
{
    
console.log(`GPU VERSION (should match CPU version) `);
 
    
device.queue.writeBufferweightsBuffer,     0, new Float32Arrayweights.flat(4) ) );
    
device.queue.writeBufferbiasesBuffer,      0, new Float32Arraybiases.flat(4)  ) );
    
await device.queue.onSubmittedWorkDone();
  
      
let cost 0;
    for (
let j 0xordataset.lengthj++) {
      
let o await activateGPU(  xordataset[j].inputs );
      for (
let b=0b<xordataset[j].outputs.lengthb++)
      {
        
cost += Math.powxordataset[j].outputs[b] - o[b], 2);
      }
    }
    
cost /= 4;
    
console.log(`total error (no training) mean squared error: ${cost}`);
  

    for (
let i 0xordataset.lengthi++) 
    {
        const 
result await activateGPUxordataset[i].inputs );

        
console.log(`for input ${xordataset[i].inputs} expected ${xordataset[i].outputs} predicted ${result[0].toFixed(4)} which is ${Math.round(result[0]) === xordataset[i].outputs[0] ? "correct" "incorrect"}`);
    }
}

//---------------------------------------------------------------------------

async function trainGPUNetwork()
{  
    
console.log(`
    ----------TRAIN WITH GPU VERSION-------------
    
`);

    
device.queue.writeBufferweightsBuffer,     0, new Float32Arrayweights.flat(4) ) );
    
device.queue.writeBufferbiasesBuffer,      0, new Float32Arraybiases.flat(4)  ) );
    
await device.queue.onSubmittedWorkDone();
  
    for (
let epoch 0epoch <= 1000epoch++) {
        
let indexes = Array.from(Array( xordataset.length ).keys());
        
indexes.sort(() => Math.random() - 0.5);
        for (
let j of indexes) {
            
await activateGPUxordataset[j].inputs ); 
            
await propagateGPUxordataset[j].outputsLEARNING_RATE);
        }

        if (
epoch 100 === 0) {
            
let cost 0;
            for (
let j 0xordataset.lengthj++) {
                
let o await activateGPU(  xordataset[j].inputs );
                for (
let b=0b<xordataset[j].outputs.lengthb++)
                {
                    
cost += Math.powxordataset[j].outputs[b] - o[b], 2);
                }
            }
            
cost /= 4;
            
console.log(`epoch ${epoch} mean squared error: ${cost}`);
        }
    }


    for (
let i 0xordataset.lengthi++) 
    {
        const 
result await activateGPUxordataset[i].inputs );

        
console.log(`for input ${xordataset[i].inputs} expected ${xordataset[i].outputs} predicted ${result[0].toFixed(4)} which is ${Math.round(result[0]) === xordataset[i].outputs[0] ? "correct" "incorrect"}`);
    }
  
  
  
      
copyBufferweightsBufferweights );
      
copyBufferbiasesBuffer,  biases );
  
}

//---------------------------------------------------------------------------

async function copyBuffer(gpuBuffcpuBuff)
{
  
/*
  Note: - make sure you add the 'GPUBufferUsage.COPY_SRC' flag to the buffer creation (copy the buffer data back)
  */
  // Retrieve results
  
const readBuffer device.createBuffer({sizegpuBuff.sizeusageGPUBufferUsage.COPY_DST GPUBufferUsage.MAP_READ });

  const 
commandEncoder device.createCommandEncoder();
  
commandEncoder.copyBufferToBuffer(gpuBuff0readBuffer0gpuBuff.size);
  
device.queue.submit([commandEncoder.finish()]);

  
await readBuffer.mapAsync(GPUMapMode.READ);
  const 
res = Array.from( new Float32Array(readBuffer.getMappedRange()) );
  
cpuBuff.length res.length;
  
cpuBuff.forEach( (_,i)=>{cpuBuff[i]=res[i]; } );
  
readBuffer.unmap();
}

//---------------------------------------------------------------------------

async function dumpBuffer(buffstr='data:')
{
  
/*
  Note: - make sure you add the 'GPUBufferUsage.COPY_SRC' flag to the buffer creation (copy the buffer data back)
  */
  // Retrieve results
  
const readBuffer device.createBuffer({sizebuff.sizeusageGPUBufferUsage.COPY_DST GPUBufferUsage.MAP_READ });

  const 
commandEncoder device.createCommandEncoder();
  
commandEncoder.copyBufferToBuffer(buff0readBuffer0buff.size);
  
device.queue.submit([commandEncoder.finish()]);

  
await readBuffer.mapAsync(GPUMapMode.READ);
  const 
results = Array.from( new Float32Array(readBuffer.getMappedRange()) );
  
readBuffer.unmap();
  
  
console.logstrresults );
}

//---------------------------------------------------------------------------

/*
but( 'load config', loadConfig );
but( 'save config', saveConfig );
but( 'reset weights and biases', initializeWeightsandBiases );
but( 'train JS network', trainJSNetwork );
but(' output JS network', outputJSNetwork );
but( 'train GPU network', trainGPUNetwork );
but(' output GPU network', outputGPUNetwork );
*/

//---------------------------------------------------------------------------


//--------------------------------------------------------------------------------------



xinitialize = ( args = { layers:[1,2,1], build:'cpu'iterations:10learningrate:0.2 } ) => {

    if ( 
args.layers       xlayers       args.layers;
    if ( 
args.build        xbuild        args.build;
    if ( 
args.iterations   xiterations   args.iterations;
    if ( 
args.learningrate xlearningrate args.learningrate;

    if ( 
args.learningrate LEARNING_RATE args.learningrate;
    if ( 
args.layers       layers args.layers;
  
    
maxLayerSize = [...layers].sort( (a,b)=>b-)[0];

    
weights  = Array( layers.length maxLayerSize maxLayerSize ).fill);
    
biases   = Array( layers.length maxLayerSize ).fill(0);
    
loutputs = Array( layers.length maxLayerSize ).fill(0);
    
errors   = Array( layers.length maxLayerSize ).fill(0);

    
MAX_NEURONS_PER_LAYER maxLayerSize;
    
NUM_LAYERS            layers.length;
  
    
initializeWeightsandBiases();
  
    
stConfig = {
      
MAX_NEURONS_PER_LAYER maxLayerSize,  // u32,
      
NUM_LAYERS            NUM_LAYERS,    // u32,
      
LEARNING_RATE         LEARNING_RATE*1000000// f32, // 0.1, 0.2, ...
      
ACTIVATION_TYPE       0              // u32  // enum - 0-sig, 1-relu, 2-reluleaky, ...
    
}

    
configBuffer    createBuffer(new Uint32ArrayObject.values(stConfig) ), GPUBufferUsage.STORAGE );
    
layersBuffer    createBuffer(new Uint32Arraylayers ), GPUBufferUsage.STORAGE );
    
inputsBuffer    createBuffer(new Float32Arraylayers[0]  ), GPUBufferUsage.STORAGE );
    
resultsBuffer   createBuffer(new Float32Arraylayers[layers.length-1] ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC );
    
expectedBuffer  createBuffer(new Float32Arraylayers[layers.length-1] ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC );
    
weightsBuffer   createBuffer(new Float32Arrayweights.flat(4) ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
    
biasesBuffer    createBuffer(new Float32Arraybiases.flat(4)  ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
    
outputsBuffer   createBuffer(new Float32Arrayloutputs.flat(4) ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
    
errorsBuffer    createBuffer(new Float32Arrayerrors.flat(4) ), GPUBufferUsage.STORAGE GPUBufferUsage.COPY_SRC);
  
 
}


xactivate async ipp ) => {
    if ( 
xbuild == 'cpu' ) {
          return 
activateipp );         
    }
    else if ( 
xbuild == 'gpu' ) {
          return 
await activateGPUipp ); 
    }
    
console.assert('unknown build');
};

xpropagate async op ) => {
    if ( 
xbuild == 'cpu' ) {
          return 
propagateop );
    }
      if ( 
xbuild == 'gpu' ) {
          return 
propagateGPUop );
    }
    
console.assert('unknown build');
};

//--------------------------------------------------------------------------------------





Resources and Links


• WebGPU Lab Neural Network Modular with Config [LINK]
















WebGPU by Example: Fractals, Image Effects, Ray-Tracing, Procedural Geometry, 2D/3D, Particles, Simulations WebGPU Compute graphics and animations using the webgpu api 12 week course kenwright learn webgpu api kenwright programming compute and graphics applications with html5 and webgpu api kenwright real-time 3d graphics with webgpu kenwright webgpu api develompent a quick start guide kenwright webgpu by example 2022 kenwright webgpu gems kenwright webgpu interactive compute and graphics visualization cookbook kenwright wgsl webgpu shading language cookbook kenwright wgsl webgpugems shading language cookbook kenwright



 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.