Code viewer for World: Practical n.2 ANN (clone b...

// Cloned by Abdelshafa Abdala on 2 Dec 2022 from World "Practical n.2 ANN" by Stefano Marzo 
// Please leave this clone trail here.
 
function loadCss(t){var e=document.getElementsByTagName("head")[0],n=document.createElement("link");n.rel="stylesheet",n.type="text/css",n.href=t,n.media="all",e.appendChild(n)}const PIXELS=28;let canvas,ctx,nn,mnist,flag=!1,prevX=0,currX=0,prevY=0,currY=0,dot_flag=!1,scaleC=6,x="black",y=2;function init(){canvas=document.getElementById("can"),ctx=canvas.getContext("2d"),w=canvas.width,h=canvas.height,canvas.addEventListener("mousemove",function(t){findxy("move",t)},!1),canvas.addEventListener("mousedown",function(t){findxy("down",t)},!1),canvas.addEventListener("mouseup",function(t){findxy("up",t)},!1),canvas.addEventListener("mouseout",function(t){findxy("out",t)},!1)}function draw(){ctx.beginPath(),ctx.moveTo(prevX/scaleC,prevY/scaleC),ctx.lineTo(currX/scaleC,currY/scaleC),ctx.strokeStyle=x,ctx.lineWidth=y,ctx.stroke(),ctx.closePath()}function erase(){ctx.clearRect(0,0,w,h),$("#predictionDiv").html("")}function findxy(t,e){"down"==t&&(prevX=currX,prevY=currY,currX=e.clientX-canvas.offsetLeft,currY=e.clientY-canvas.offsetTop,flag=!0,(dot_flag=!0)&&(ctx.beginPath(),ctx.fillStyle=x,ctx.fillRect(currX,currY,2,2),ctx.closePath(),dot_flag=!1)),"up"!=t&&"out"!=t||(flag=!1),"move"==t&&flag&&(prevX=currX,prevY=currY,currX=e.clientX-canvas.offsetLeft,currY=e.clientY-canvas.offsetTop,draw())}class ANN{constructor(t=[2,2,2],e=[sigmoid,sigmoid],n=.2,i=!1){this.structure=t,this.activation=e,this.lr=n,this.bias=i,this.x=this.generateLayers(),this.w=this.generateWeights(),this.activation.splice(0,0,null),this.trainingExampleFed=0,this.testingExampleFed=0,this.rightPrediction=0}generateLayers(){let t=[];for(let e in this.structure)t.push(new Matrix(this.structure[e],1));return t}generateWeights(){let t=this.structure.length-1,e=new Array(t-1);e[0]=null;for(let n=0;n<t;n++){let t=this.x[n].rows,i=this.x[n+1].rows;e[n+1]=new Matrix(i,t),e[n+1].randomize()}return e}net(t){return Matrix.multiply(this.w[t],this.x[t-1])}act(t){return this.net(t).map(this.activation[t].func)}onlyPropagate(t){t=this.transformInput(t),this.x[0]=t;for(let t=1;t<this.x.length;t++)this.x[t]=this.act(t)}propagate(t){this.x[0]=t;for(let t=1;t<this.x.length;t++)this.x[t]=this.act(t)}calculateError(t){let e=0;for(let n in t.data)for(let i in t.data[n])e+=Math.pow(t.data[n][i]-this.x[this.x.length-1].data[n][i],2)/2;return e}backpropagate(t){let e=[],n=Matrix.subtract(t,this.x[this.x.length-1]),i=Matrix.map(this.x[this.x.length-1],this.activation[this.x.length-1].dfunc);i.multiply(n),i.multiply(this.lr);let s=Matrix.multiply(i,Matrix.transpose(this.x[this.x.length-2]));this.w[this.x.length-1].add(s),e[this.x.length-1]=n;for(let t=this.x.length-2;t>0;t--){let n=Matrix.multiply(Matrix.transpose(this.w[t+1]),e[t+1]),i=Matrix.map(this.x[t],this.activation[t].dfunc);i.multiply(n),i.multiply(this.lr);let s=Matrix.multiply(i,Matrix.transpose(this.x[t-1]));this.w[t].add(s),e[t]=n}}backpropagateCodingTrain(t){let e=[],n=Matrix.subtract(t,this.x[this.x.length-1]),i=Matrix.map(this.x[this.x.length-1],this.activation[this.x.length-1].dfunc);i.multiply(n),i.multiply(this.lr);let s=Matrix.multiply(i,Matrix.transpose(this.x[this.x.length-2]));this.w[this.x.length-1].add(s),e[this.x.length-1]=n;for(let t=this.x.length-2;t>0;t--){let n=Matrix.multiply(Matrix.transpose(this.w[t+1]),e[t+1]),i=Matrix.map(this.x[t],this.activation[t].dfunc);i.multiply(n),i.multiply(this.lr);let s=Matrix.multiply(i,Matrix.transpose(this.x[t-1]));this.w[t].add(s),e[t]=n}}train(t,e){t=this.transformInput(t),e=this.transformtarget(e),this.propagate(t),this.backpropagate(e);let n=outNumeric(e),i=this.getPrediction();this.trainingExampleFed+=1,n==i&&(this.rightPrediction+=1),console.log("error: ",this.calculateError(e).toFixed(4)," target: ",n," prediction: ",i," examples fed: ",this.trainingExampleFed,n==i?"<- CORRECT PREDICTION":" ")}test(t,e){t=this.transformInput(t),e=this.transformtarget(e),this.propagate(t);let n=outNumeric(e),i=this.getPrediction();this.testingExampleFed+=1,n==i&&(this.rightPrediction+=1),console.log("error: ",this.calculateError(e).toFixed(4)," target: ",n," prediction: ",i," precision: ",this.rightPrediction,"/",this.testingExampleFed,n==i?"GOT IT":" ")}getPrediction(){return outNumeric(this.x[this.x.length-1])}transformInput(t){return t=(t=Array.from(t)).map(t=>t/255),t=Matrix.fromArray(t)}transformtarget(t){return t=outOneHot(this.structure[this.structure.length-1],t),t=Matrix.fromArray(t)}generateHtml(){return'\n        <div id="ANNGenerator"><h4>Generalized ANN</h4>\n        <p class="ANNSubtitle"><i>customize it</i></p>\n        <div id="LRChanger">Learning Rate = \n        <span onclick="nn.showLRChanger()" class="jsLink">'+this.lr+"</span>\n        </div>\n        <br>\n            "+this.generateInputSection()+"\n            "+this.generateHiddenSections()+"\n            "+this.generateOutputSection()+"\n        </div>\n      "}showLRChanger(){$("#LRChanger").html('\n            Learning rate = \n            <input type="number" id="ANNLR" value="'+this.lr+'" />\n            <button onclick="nn.changeLR()">Save</button>\n        ')}changeLR(){this.lr=Number($("#ANNLR").val()),$("#ANNDiv").html(this.generateHtml())}generateInputSection(){return'\n        <div class="ANNSection">\n                <div class="ANNTitleContainer">\n                    <span class="ANNTitle"><b>Input layer</b></span>\n                    <span class="ANNFunction"></span>\n                </div>\n                <div class="ANNCenter"># of Neurons: <b>'+this.structure[0]+'</b></div>\n                <div class="ANNNeurons"><b><i>I</i></b><sub>0</sub> ... <b><i>I</i></b><sub>'+(this.structure[0]-1)+"</sub></div>\n            </div>\n      "}generateHiddenSection(t){return'\n        <div class="ANNSectionAdd" onclick="nn.createNewLayer('+t+')">\n        <b>+</b>\n        </div>\n        <div class="ANNSection" id="hiddenLayer'+t+'">\n                <div class="ANNTitleContainer">\n                    <span class="ANNTitle"><b>Hidden '+t+' layer</b></span>\n                    <span class="ANNFunction">f = '+this.activation[t].name+'</span>\n                </div>\n                <div class="ANNCenter"># of Neurons: <b>'+this.structure[t]+'</b></div>\n                <div class="ANNNeurons">\n                <span class="ANNNeuronNames">\n                <b><i>H'+t+"</i></b><sub>0</sub> ... \n                <b><i>H"+t+"</i></b><sub>"+(this.structure[t]-1)+'</sub>\n                </span>\n                <span class="ANNSettings">\n                <span class="jsLink" onclick="$(\'#hiddenLayer'+t+"').html(nn.generateHiddenSectionSettings("+t+'))">change settings</span>\n                <span>\n                </div>\n            </div>\n      '}generateHiddenSectionSettings(t){return'\n            <div class="ANNSection" id="hiddenLayer'+t+'">\n            <div class="ANNTitleContainer">\n                <span class="ANNTitle"><b>Hidden '+t+' layer</b></span>\n                <span class="ANNFunction">'+this.activationFunctionSelection("hidFunction"+t)+'</span>\n            </div>\n            <div class="ANNCenter"># of Neurons: \n            <input type="number" id="hidNumber'+t+'" value="'+this.structure[t]+'">\n            </div>\n            <div class="ANNNeurons">\n            <span class="ANNNeuronNames">\n            <b><i>H'+t+"</i></b><sub>0</sub> ... \n            <b><i>H"+t+"</i></b><sub>"+(this.structure[t]-1)+'</sub>\n            </span>\n            <span class="ANNSettings">\n            <span class="jsLink" onclick="nn.updateHiddenSettings('+t+", $('#hidNumber"+t+"').val(), $('#hidFunction"+t+"').val())\">save settings</span>\n            <span>\n            </div>\n        </div>\n    "}updateHiddenSettings(t,e,n){this.structure[t]=Number(e),this.activation[t]=allFunctions[Number(n)],this.x=this.generateLayers(),this.w=this.generateWeights(),this.writeHtmlOnPage()}generateOutputSectionSettings(){return'\n                <div class="ANNTitleContainer">\n                    <span class="ANNTitle"><b>Output layer</b></span>\n                    <span class="ANNFunction">'+this.activationFunctionSelection("outFunction")+' </span>\n                </div>\n                <div class="ANNCenter"># of Neurons: \n                <input type="number" id="outNumber" value="'+this.structure[this.structure.length-1]+'">\n                </div>\n                <div class="ANNNeurons">\n                <span class="ANNNeuronNames">\n                <b><i>O</i></b><sub>0</sub> ... \n                <b><i>O</i></b><sub>'+(this.structure[this.structure.length-1]-1)+'</sub>\n                </span>\n                <span class="ANNSettings">\n                <span class="jsLink" onclick="nn.updateOutputSettings($(\'#outNumber\').val(), $(\'#outFunction\').val())">save settings</span>\n                <span>\n                </div>\n      '}generateHiddenSections(){let t="";for(let e=1;e<this.structure.length-1;e++)t+=this.generateHiddenSection(e);return t}generateOutputSection(){return'\n        <div class="ANNSectionAdd" onclick="nn.createNewLayer('+(this.structure.length-1)+')">\n        <b>+</b>\n        </div>\n        <div class="ANNSection" id="outputLayer">\n                <div class="ANNTitleContainer">\n                    <span class="ANNTitle"><b>Output layer</b></span>\n                    <span class="ANNFunction">f = '+this.activation[this.structure.length-1].name+' </span>\n                </div>\n                <div class="ANNCenter"># of Neurons: <b>'+this.structure[this.structure.length-1]+'</b></div>\n                <div class="ANNNeurons">\n                <span class="ANNNeuronNames">\n                <b><i>O</i></b><sub>0</sub> ... \n                <b><i>O</i></b><sub>'+(this.structure[this.structure.length-1]-1)+'</sub>\n                </span>\n                <span class="ANNSettings">\n                <span class="jsLink" onclick="$(\'#outputLayer\').html(nn.generateOutputSectionSettings())">change settings</span>\n                <span>\n                </div>\n            </div>\n      '}generateOutputSectionSettings(){return'\n                <div class="ANNTitleContainer">\n                    <span class="ANNTitle"><b>Output layer</b></span>\n                    <span class="ANNFunction">'+this.activationFunctionSelection("outFunction")+' </span>\n                </div>\n                <div class="ANNCenter"># of Neurons: \n                <input type="number" id="outNumber" value="'+this.structure[this.structure.length-1]+'">\n                </div>\n                <div class="ANNNeurons">\n                <span class="ANNNeuronNames">\n                <b><i>O</i></b><sub>0</sub> ... \n                <b><i>O</i></b><sub>'+(this.structure[this.structure.length-1]-1)+'</sub>\n                </span>\n                <span class="ANNSettings">\n                <span class="jsLink" onclick="nn.updateOutputSettings($(\'#outNumber\').val(), $(\'#outFunction\').val())">save settings</span>\n                <span>\n                </div>\n      '}updateOutputSettings(t,e){this.structure[this.structure.length-1]=Number(t),this.activation[this.activation.length-1]=allFunctions[Number(e)],this.x=this.generateLayers(),this.w=this.generateWeights(),this.writeHtmlOnPage()}activationFunctionSelection(t){let e="";for(let t in allFunctions)e+='<option value="'+t+'">'+allFunctions[t].name+"</option>";return'\n        <label for="'+t+'">f = </label>\n        <select id="'+t+'">\n        '+e+"\n        </select> \n        "}createNewLayer(t){this.structure.splice(t,0,2),this.activation.splice(t,0,allFunctions[0]),this.x=this.generateLayers(),this.w=this.generateWeights(),this.writeHtmlOnPage()}writeHtmlOnPage(){$("#ANNDiv").html(this.generateHtml())}}loadCss("https://ancientbrain.com/uploads/stefano/general_ann_style.css"),loadCss("https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@1,700&family=Nunito:wght@300&display=swap");let n_unseen=300;function getUnseenScore(){let t=[];for(let e=0;e<mnist.test_images.length;e++)t.push(e);let e=[];for(let n=0;n<n_unseen;n++){let n=Math.floor(Math.random()*t.length);e.push(t.splice(n,1)[0])}let n=0;for(let t=0;t<n_unseen;t++)nn.onlyPropagate(mnist.test_images[e[t]]),nn.getPrediction()==mnist.test_labels[e[t]]&&(n+=1);$("#scoreDiv").html($("#scoreDiv").html()+"<br>Score on unseen data: "+(n/n_unseen*100).toFixed(2)+"%")}function loadData(){loadMNIST(function(t){mnist=t,console.log("All data loaded into mnist object"),AB.removeLoading(),console.log("%c Remember: this project does not need the console to be executed. \n For a better experience do not use the console while drawing, \n TIP: scroll to the top of the page before drawing in the canvas. ","background: #222; color: #bada55; font-size:15px")})}$.getScript("/uploads/codingtrain/matrix.js",function(){$.getScript("/uploads/stefano/utils.js",function(){$.getScript("/uploads/stefano/generalANN.js",function(){$.getScript("/uploads/codingtrain/mnist.js",function(){console.log("All JS loaded"),loadData(),(nn=new ANN([784,64,10])).writeHtmlOnPage()})})})});let epoch_size=20,tests_number=100,trains_number=6*tests_number,epoch=0;const max_epochs=6e4/epoch_size;let super_epoch=15,scoreArray=[];function executeEpochTraining(){let t=epoch*epoch_size;if(epoch<max_epochs){for(let e=t;e<t+epoch_size;e++)nn.train(mnist.train_images[e],mnist.train_labels[e]);epoch+=1,scoreArray.push({epoch:epoch,value:(nn.rightPrediction/nn.trainingExampleFed*100).toFixed(2)})}else console.log("Training completed")}function executeSuperEpoch(){if(AB.removeLoading(),AB.loadingScreen(),epoch<max_epochs)for(let t=0;t<super_epoch;t++)executeEpochTraining();createGraph(),getUnseenScore(),AB.removeLoading()}function createGraph(){$("#my_dataviz").html(""),data=scoreArray;let t=10,e=30,n=40,i=40,s=500-i-e,a=300-t-n,r=d3.select("#my_dataviz").append("svg").attr("width",s+i+e).attr("height",a+t+n).append("g").attr("transform","translate("+i+","+t+")"),l=d3.scaleLinear().domain([0,100]).range([a,0]);r.append("g").call(d3.axisLeft(l));let o=d3.scaleLinear().domain([1,max_epochs]).range([0,s]);r.append("g").attr("transform","translate(0,"+a+")").call(d3.axisBottom(o)),r.append("path").datum(data).attr("fill","none").attr("stroke","steelblue").attr("stroke-width",2).attr("d",d3.line().x(function(t){return o(t.epoch*epoch_size)}).y(function(t){return l(t.value)})),r.append("text").attr("transform","rotate(-90)").attr("y",0-i).attr("x",0-a/2).attr("dy",".8em").style("text-anchor","middle").text("Score"),r.append("text").attr("transform","translate("+s/2+" ,"+(a+t+25)+")").attr("dx",".8em").style("text-anchor","middle").text("Epoch"),void 0!=nn&&$("#scoreDiv").html("Current score: "+(nn.rightPrediction/nn.trainingExampleFed*100).toFixed(2)+"%<br>Example fed: "+nn.trainingExampleFed)}function resetANN(){location.reload()}function executeTraining(){for(let t=0;t<trains_number;t++)nn.train(mnist.train_images[t],mnist.train_labels[t])}function executeTesting(){for(let t=0;t<tests_number;t++)nn.test(mnist.test_images[t],mnist.test_labels[t])}function predictInput(){let t=ctx.getImageData(0,0,w,h),e=[];for(let n=0;n<t.data.length;n++)(n+1)%4==0&&e.push(t.data[n]);let n=new Uint8Array(e);nn.onlyPropagate(n);let i=nn.getPrediction();$("#predictionDiv").html("Prediction: "+i),console.log("The drawn number seems to be: "+i)}$.getScript("https://d3js.org/d3.v4.js",function(){console.log("D3JS loaded"),createGraph()}),document.write('\n\n<html>\n    <body onload="init()">\n        <div style="position:relative; display:inline-block">\n        <div id="ANNDiv" style="position:relative; display:inline-block"></div>\n        <div style="display:block">\n        <button onclick="executeSuperEpoch()" style="display:inline-block; width:150px; margin-top:15px">Feed '+epoch_size*super_epoch+' examples</button>\n        <button onclick="resetANN()" style="display:inline-block; width:90px; margin-top:15px">Reset ANN</button>\n        </div>\n        </div>\n        <div style="display:inline-block">\n        <div id="my_dataviz" style="display:inline-block"></div>\n        <div id="scoreDiv" style="display:block; margin-top:20px">Current score: 0%</div>\n        </div>\n        Draw\n        <canvas id="can" width="28" height="28" style="margin:20px;position:relative;border:2px solid;width:'+28*scaleC+"px; height:"+28*scaleC+'px;display:inline-block"></canvas>\n        <div style="display:inline-block">\n        <button id="clr" onclick="erase()" style="position:relative; display:block">Clear</button>\n        <button id="clr" onclick="predictInput()" style="position:relative; display:block">Predict</button>\n        <div id="predictionDiv"></div>\n        </div>\n    </body>\n    </html>\n\n');