Code viewer for World: A-Z Character Recognition ...

// Cloned by Abdelshafa Abdala on 1 Dec 2022 from World "A-Z Character Recognition" by Paul R 
// Please leave this clone trail here.
 
var layer_defs,net,trainer;let trainrun=1,train_index=0,total_trains=0,autoTestsAt=-1;var xLossWindow=new ErrorStore(100),wLossWindow=new ErrorStore(100);let testrun=1,test_index=0,total_tests=0,total_correct=0;const PIXELS=28,PIXELSSQUARED=PIXELS*PIXELS,ZOOMFACTOR=7,ZOOMPIXELS=7*PIXELS,canvaswidth=PIXELS+ZOOMPIXELS+50,canvasheight=4*ZOOMPIXELS+150,DOODLE_THICK=18,DOODLE_BLUR=3;let do_training=!0;const TRAINPERSTEP=30;var jsLoaded=!1;let doodle,doodle_exists=!1,mousedrag=!1;var doodle_inputs,lossChart,testChart,showGraphs=!0;const letters=["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];var thehtml;AB.headerCSS({"max-height":"95vh"}),thehtml="<hr> <h2> 1. Doodle </h2>  Draw your doodle in top LHS. <button onclick='wipeDoodle();' class='normbutton' >Clear doodle</button><br> ",AB.msg(thehtml,1),thehtml="<table> <tr><th></th><th>Letter</th><th>Confidence</th><th></th><th>Letter</th><th>Confidence</th></tr>",thehtml+="<tr><td>First Guess</td><td id='guess_letter_1'></td><td id='guess_percent_1'></td><td>Fourth Guess</td><td id='guess_letter_4'></td><td id='guess_percent_4'></td></tr>",thehtml+="<tr><td>Second Guess</td><td id='guess_letter_2'></td><td id='guess_percent_2'></td><td>Fifth Guess</td><td id='guess_letter_5'></td><td id='guess_percent_5'></td></tr>",thehtml+="<tr><td>Third Guess</td><td id='guess_letter_3'></td><td id='guess_percent_3'></td><td>Sixth Guess</td><td id='guess_letter_6'></td><td id='guess_percent_6'></td></tr></table>",thehtml+="<hr>Draw a doodle. Type the correct answer in the box and then click 'Save doodle'<input type='text' id='doodlePred' size='1' value='A'><button onclick='saveDoodle();' class='normbutton' >Save doodle</button> &nbsp; &nbsp; &nbsp; &nbsp;       <button onclick='loadDoodlesAndTest();' class='normbutton' >Load doodles and test</button> Results: <div id='doodles_Results' style='color:darkgreen'>results will be displayed here</div>",AB.msg(thehtml,2),thehtml="<hr> <h2> 2. Training & Testing </h2>  <button id='pauseBtn' onclick='pauseTraining()' class='normbutton' >Pause training</button> <br> ",thehtml+="<table> <tr><th><div style='width: 150px;'>Training</div></th><th>Testing (Last 100)</th><th>Testing (Best 100)</th></tr>",thehtml+="<tr><td id = 'training_run'></td><td id='testing_last'></td><td id='testing_best'></td></tr>",thehtml+="<tr><td id = 'training_num'></td><td></td><td></td></tr></table>",AB.msg(thehtml,3),thehtml="<hr> A set from the test data. Useful for comparing results.<br>  <button onclick='controlTest();' class='normbutton' >Run control test</button> <br> ",thehtml+="<table> <tr>";for(var trainingData_1,trainingData_2,testingData,controlTestData,rowHtml="",l=0;l<26;l++)thehtml=thehtml+"<th>"+letters[l]+"</th>",rowHtml=rowHtml+"<td id='ctrlTest_"+l+"'>0</td>";function preload(){loadJSON("/uploads/paul79/training_images_letters_1.json",function(t){console.log("JSON Data 1 finished reading."),console.log("JSON Data 1 size: "+t.size),trainingData_1=t,loadJSON("/uploads/paul79/training_images_letters_2.json",function(t){console.log("JSON Data 2 finished reading."),console.log("JSON Data 2 size: "+t.size),trainingData_2=t,loadJSON("/uploads/paul79/training_images_letters_3.json",function(t){console.log("JSON Data 3 finished reading."),console.log("JSON Data 3 size: "+t.size),testingData=t,loadJSON("/uploads/paul79/control_set_letters_26.json",function(t){console.log("JSON Data 4 finished reading."),console.log("JSON Data 4 size: "+t.size),controlTestData=t,AB.removeLoading()})})})})}function setup(){createCanvas(canvaswidth,canvasheight),(doodle=createGraphics(ZOOMPIXELS,ZOOMPIXELS)).pixelDensity(1),$.getScript("/uploads/paul79/convnet.js",function(){$.getScript("/uploads/paul79/LineChart.js",function(){console.log("All JS loaded"),jsLoaded=!0,(layer_defs=[]).push({type:"input",out_sx:28,out_sy:28,out_depth:1}),layer_defs.push({type:"conv",sx:5,filters:8,stride:1,pad:2,activation:"relu"}),layer_defs.push({type:"pool",sx:2,stride:2}),layer_defs.push({type:"conv",sx:5,filters:16,stride:1,pad:2,activation:"relu"}),layer_defs.push({type:"pool",sx:3,stride:3}),layer_defs.push({type:"softmax",num_classes:26}),(net=new convnetjs.Net).makeLayers(layer_defs),trainer=new convnetjs.SGDTrainer(net,{method:"adadelta",batch_size:20,l2_decay:.001})})})}function getImage(t){let e=createImage(PIXELS,PIXELS);e.loadPixels();for(let s=0;s<PIXELSSQUARED;s++){let n=t[s],a=4*s;e.pixels[a+0]=n,e.pixels[a+1]=n,e.pixels[a+2]=n,e.pixels[a+3]=255}return e.updatePixels(),e}function getInputs(t){var e=new convnetjs.Vol(28,28,1,0);for(let s=0;s<PIXELSSQUARED;s++){let n=t[s];e.w[s]=n/255}return e}function train(t){var e=trainrun%2==1?trainingData_1:trainingData_2;let s=e.images[train_index].img,n=e.images[train_index].label;if(t){var a=getImage(s);image(a,0,ZOOMPIXELS+50,ZOOMPIXELS,ZOOMPIXELS),image(a,ZOOMPIXELS+50,ZOOMPIXELS+50,PIXELS,PIXELS)}let o=getInputs(s);var r=trainer.train(o,n),i=r.cost_loss,l=r.l2_decay_loss;if(xLossWindow.add(i),wLossWindow.add(l),document.getElementById("training_run").innerHTML="Train Run: "+trainrun,document.getElementById("training_num").innerHTML="Train Tests: "+train_index,++train_index>=e.images.length&&(train_index=0,console.log("finished trainrun: "+trainrun),trainrun++),showGraphs&&total_trains%200==0){var d=xLossWindow.get_average(),h=wLossWindow.get_average();d>=0&&h>=0&&createLossChart(d+h,total_trains)}total_trains++}function pauseTraining(){do_training?(do_training=!1,document.getElementById("pauseBtn").innerHTML="Resume Training"):(do_training=!0,document.getElementById("pauseBtn").innerHTML="Pause Training")}thehtml=(thehtml+="<th>Result</th>")+"</tr><tr>"+(rowHtml+="<td id='ctrlTest_Result'>0%</td>")+"</tr></table>",AB.msg(thehtml,4),thehtml="<hr> <h2> 3. Save/Load Snapshot </h2> Save current state to a snapshot. Load a previously saved snapshot <br>   <button onclick='saveSnapshot();' class='normbutton' >Save snapshot</button> <button onclick='loadSnapshot();' class='normbutton' >Load snapshot</button><br> <br> Download a snapshot - 60k trainings <br> <button onclick='downloadSnapshot();' class='normbutton' >Download snapshot</button>",AB.msg(thehtml,5);var testResults=new TestResultsSquash(100,10),testingBest=0;function test(){let t=testingData.images[test_index].img,e=testingData.images[test_index].label,s=getInputs(t);net.forward(s);var n=net.getPrediction();test_index++,total_tests++,n==e&&total_correct++,showGraphs&&(testResults.add(n==e?1:0),total_tests%10==0&&createTestChart()),test_index%100==0&&(res=testResults.getLast(),-1!=res&&(res=Math.round(res),document.getElementById("testing_last").innerHTML=res+"%",res>testingBest&&(testingBest=res,document.getElementById("testing_best").innerHTML=res+"%"))),test_index>=testingData.images.length&&(test_index=0)}function controlTest(){for(var t=0,e=0;e<26;e++){let n=controlTestData.images[e].img,a=controlTestData.images[e].label,o=getInputs(n);net.forward(o);var s=0;net.getPrediction()==a&&(s=1,t++),document.getElementById("ctrlTest_"+e).innerHTML=s,document.getElementById("ctrlTest_Result").innerHTML=Math.round(t/26*100)+"%"}}function draw(){if(!1!==jsLoaded&&void 0!==testingData){if(background("black"),do_training)for(let t=0;t<TRAINPERSTEP;t++)if(train(0==t),test(),-1!==autoTestsAt&&total_trains%autoTestsAt==0){let t=[];for(let e=0;e<10;e++)t.push(loadDoodlesAndTest());let e=t.reduce((t,e)=>t+e,0)/t.length;console.log("Auto tests after "+total_trains+" trains:"),console.log("Average Doodle percentage: "+e);let s=testResults.getLastN(5);e=s.reduce((t,e)=>t+e,0)/s.length,console.log("Average Test percentage: "+e)}if(void 0!==lossChart&&lossChart.show(),void 0!==testChart&&testChart.show(),doodle_exists&&(drawDoodle(),guessDoodle()),mouseIsPressed){var t=ZOOMPIXELS+20;mouseX<t&&mouseY<t&&pmouseX<t&&pmouseY<t&&(doodle_exists=!0,mousedrag=!0,doodle.stroke("white"),doodle.strokeWeight(DOODLE_THICK),doodle.line(mouseX,mouseY,pmouseX,pmouseY))}else mousedrag&&(mousedrag=!1,doodle.filter(BLUR,DOODLE_BLUR))}}function drawDoodle(){let t;t=doodle.get(),image(t,0,0,ZOOMPIXELS,ZOOMPIXELS),image(t,ZOOMPIXELS+50,0,PIXELS,PIXELS)}function guessDoodle(){let t;(t=doodle.get()).resize(PIXELS,PIXELS),t.loadPixels();var e=new convnetjs.Vol(28,28,1,0);for(let s=0;s<PIXELSSQUARED;s++){t[s];e.w[s]=t.pixels[4*s]/255}for(var s=net.forward(e),n=[],a=0;a<s.w.length;a++)n.push({k:a,p:s.w[a]});n.sort(function(t,e){return t.p<e.p?1:-1});for(var o=0;o<6;o++){var r=Math.round(100*n[o].p);document.getElementById("guess_percent_"+(o+1)).innerHTML=r+"%",document.getElementById("guess_letter_"+(o+1)).innerHTML=letters[n[o].k]}}function wipeDoodle(){doodle_exists=!1,doodle.background("black")}function saveDoodle(){let t=doodle.get();t.resize(PIXELS,PIXELS),t.loadPixels();let e=document.getElementById("doodlePred").value;var s,n={pixels:[...t.pixels],label:e};(s=null===localStorage.getItem("doodles")?{doodles:[]}:JSON.parse(localStorage.getItem("doodles"))).doodles.push(n),localStorage.setItem("doodles",JSON.stringify(s))}function loadDoodlesAndTest(){var t;null===localStorage.getItem("doodles")?alert("Save doodle(s), then you can load."):t=JSON.parse(localStorage.getItem("doodles")),numCorrect=0;for(let n=0;n<t.doodles.length;n++){var e=t.doodles[n],s=new convnetjs.Vol(28,28,1,0);for(let t=0;t<PIXELSSQUARED;t++)s.w[t]=e.pixels[4*t]/255;net.forward(s);net.getPrediction()==letters.indexOf(e.label)&&numCorrect++}let n=Math.round(numCorrect/t.doodles.length*100);return document.getElementById("doodles_Results").innerHTML="Doodles tested: "+t.doodles.length+" Correct: "+n+"%",n}var graphData={xData:[],yData:[]};function createLossChart(t,e){graphData.xData.push(e),graphData.yData.push(t),data=[],colors=["#ff0000"],lineLabels=["Loss (training)"],data.push([]);for(let t=0;t<graphData.xData.length;t++)data[0].push(createVector(graphData.xData[t],graphData.yData[t]));lossChart=new LineChart(data,colors,lineLabels,250,250,5,canvasheight-250,[min(graphData.xData.flat()),max(graphData.xData.flat())],[0,max(graphData.yData.flat())])}function createTestChart(){var t=testResults.getData();data=[],colors=["#0000ff"],lineLabels=["Hidden Tests"],data.push([]);for(let e=0;e<t.xData.length;e++)data[0].push(createVector(t.xData[e],t.yData[e]));testChart=new LineChart(data,colors,lineLabels,250,250,5,canvasheight-500,[min(t.xData.flat()),max(t.xData.flat())],[0,100])}function ErrorStore(t,e){this.v=[],this.size=void 0===t?100:t,this.minsize=void 0===e?20:e,this.sum=0,this.add=function(t){if(this.v.push(t),this.sum+=t,this.v.length>this.size){var e=this.v.shift();this.sum-=e}},this.get_average=function(){return this.v.length<this.minsize?-1:this.sum/this.v.length},this.reset=function(){this.v=[],this.sum=0}}function TestResultsSquash(t,e){this.vals=[],this.size=void 0===t?100:t,this.minsize=void 0===e?10:e,this.hundredTest=[],this.sum=0,this.add=function(t){this.vals.push(t),this.sum+=t,this.vals.length>=this.size&&(this.hundredTest.push(this.sum/this.size*100),this.vals=[],this.sum=0)},this.getData=function(){for(var t=[...this.hundredTest],e=[],s=0;s<t.length;s++)e.push(this.size*(s+1));return t.push(this.sum/this.vals.length*100),e.push(e[e.length-1]+this.vals.length),{xData:e,yData:t}},this.getLast=function(){return this.hundredTest.length>0?this.hundredTest[this.hundredTest.length-1]:-1},this.getLastN=function(t){return this.hundredTest.slice(-t,this.hundredTest.length)},this.reset=function(){this.vals=[],this.hundredTest=[],this.sum=0}}function saveSnapshot(){var t=JSON.stringify(net.toJSON());localStorage.setItem("cnnSnapshot",t),console.log("Save snaphot "+t)}function loadSnapshot(){var t=JSON.parse(localStorage.getItem("cnnSnapshot"));console.log("Loading snaphot "+t),resetNetFromSnapshot(t)}function downloadSnapshot(){loadJSON("/uploads/paul79/snapshot.json",function(t){var e=t;console.log("Downloaded snapshot "+e),resetNetFromSnapshot(e)})}function resetNetFromSnapshot(t){(net=new convnetjs.Net).fromJSON(t),xLossWindow.reset(),wLossWindow.reset(),trainrun=1,train_index=0,testrun=1,test_index=0,total_tests=0,total_correct=0,testResults.reset(),graphData={xData:[],yData:[]}}